home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Games / WormWars / Source / system.c < prev    next >
C/C++ Source or Header  |  2002-03-12  |  116KB  |  3,130 lines

  1. /* $Filename: WormWars/Source/system.c $
  2.  * $VER:      Worm Wars 6.9 for Amiga $
  3.  *
  4.  * © Copyright 2002 James R. Jacobs. Freely distributable.
  5.  *        _
  6.  *       //   -=AMIGA=-
  7.  *      //
  8.  * _   //
  9.  * \\ //
  10.  *  \X/
  11.  
  12. #INCLUDES ---------------------------------------------------------------- */
  13.  
  14. #include "system.h"
  15. #include "amiga.h"
  16. #include "qdraw020.h"
  17.  
  18. #include <assert.h>
  19. // #define ASSERT
  20.  
  21. /* PROTOTYPES (Amiga-only) ----------------------------------------------- */
  22.  
  23. MODULE void freefx(void);
  24. MODULE void loadthefx(void);
  25. MODULE void loadthemusic(void);
  26. MODULE void parsewb(void);
  27. MODULE void pausetimer(void);
  28. MODULE void unpausetimer(void);
  29. MODULE void preserve(SWORD x, SBYTE row, SBYTE image, UWORD amount);
  30. MODULE ABOOL beginfx(void);
  31. MODULE UBYTE ReadJoystick(UWORD joynum);
  32. MODULE ABOOL firebutton(void);
  33.  
  34. IMPORT void align(STRPTR string, SBYTE size, TEXT filler);
  35.  
  36. AGLOBAL struct Window          *HelpWindowPtr = NULL,
  37.                                *MainWindowPtr = NULL;
  38. AGLOBAL struct Menu*            MenuPtr       = NULL;
  39. AGLOBAL struct VisualInfo*      VisualInfoPtr = NULL;
  40. AGLOBAL struct timerequest*     TimerRqPtr    = NULL;
  41. AGLOBAL struct Screen*          ScreenPtr     = NULL;
  42. AGLOBAL ABOOL                   icons         = TRUE,
  43.                                 iso           = TRUE;
  44.  
  45. /* EXTERNAL VARIABLES ----------------------------------------------------- */
  46.  
  47. IMPORT  struct ExecBase*        SysBase;
  48.  
  49. IMPORT  ABOOL                   anims, modified, randomflag, turbo;
  50. IMPORT  SBYTE                   a, eachworm[4][2][9],
  51.                                 level, levels, players, sourcelevel,
  52.                                 startx[MAXLEVELS + 1], starty[MAXLEVELS + 1];
  53. IMPORT  SWORD                   secondsleft, secondsperlevel;
  54. IMPORT  ULONG                   delay, r;
  55. IMPORT  UBYTE                   board[MAXLEVELS + 1][FIELDX + 1][FIELDY + 1],
  56.                                 field[FIELDX + 1][FIELDY + 1];
  57. IMPORT    struct HiScoreStruct    hiscore[HISCORES + 1];
  58. IMPORT    struct WormStruct       worm[4];
  59. IMPORT  UBYTE                   missileframes[4][MISSILEFRAMES + 1];
  60. IMPORT  TEXT                    pathname[81],
  61.                                 date[DATELENGTH + 1],
  62.                                 times[TIMELENGTH + 1];
  63.  
  64.         struct Library*         TimerBase;
  65.         struct ASLBase*         ASLBase        = NULL;
  66. //      struct Library*         AmigaGuideBase = NULL;
  67.         struct DiskFontBase*    DiskFontBase   = NULL;
  68.         struct GadToolsBase*    GadToolsBase   = NULL;
  69.         struct GfxBase*         GfxBase        = NULL;
  70.         struct Library*         IconBase       = NULL;
  71.         struct IntuitionBase*   IntuitionBase  = NULL;
  72.         struct LowLevelBase*    LowLevelBase   = NULL;
  73.         struct MEDPlayerBase*   MEDPlayerBase  = NULL;
  74.         struct UtilityBase*     UtilityBase    = NULL;
  75.  
  76. /* MODULE VARIABLES (used only within system.c) --------------------------- */
  77.  
  78. MODULE  ABOOL  eversent[4],
  79.                ignore       = FALSE,
  80.                quiet        = FALSE;
  81. MODULE  SBYTE  AudioClosed  = TRUE,
  82.                OldPri       = 0,
  83.                TimerClosed  = TRUE;
  84. MODULE  UBYTE  fxable       = UNTRIED,
  85.                mode         = NULL,
  86.                musicable    = UNTRIED;
  87. MODULE  ULONG  fsize,
  88.                receipter[4] = {(ULONG) -1, (ULONG) -1, (ULONG) -1, (ULONG) -1};
  89. MODULE  UBYTE* fbase        = NULL;
  90. MODULE  BPTR   FilePtr      = NULL;
  91. MODULE  APTR   OldWindowPtr = NULL;
  92. MODULE  SWORD  pixy;
  93. MODULE  SBYTE  hiframe      = -1;
  94.  
  95. MODULE  struct QDPlaneInfo    QDPI;
  96. MODULE  ABOOL                 useqdraw            = FALSE;
  97.  
  98. MODULE    struct TextAttr       WormWars8 =
  99. {    (STRPTR) "WormWars.font", 8, FS_NORMAL, FPF_DISKFONT | FPF_DESIGNED
  100. },                            Topaz8 =
  101. {    (STRPTR) "topaz.font", 8, FS_NORMAL, FPF_ROMFONT | FPF_DESIGNED
  102. };
  103.  
  104. MODULE    struct Gadget         *CheckboxGadgetPtr  = NULL,
  105.                               *CycleGadgetPtr[4]  = {NULL, NULL, NULL, NULL},
  106.                               *GListPtr           = NULL,
  107.                               *PrevGadgetPtr      = NULL,
  108.                               *RandomGadgetPtr    = NULL,
  109.                               *StringGadgetPtr[5] = {NULL, NULL, NULL, NULL, NULL};
  110. MODULE    struct MsgPort        *AudioPortPtr[4]    = {NULL, NULL, NULL, NULL},
  111.                               *TimerPortPtr       = NULL;
  112. MODULE    struct timeval        *CurrentValPtr      = NULL,
  113.                               *PausedValPtr       = NULL,
  114.                               *StartValPtr        = NULL;
  115.  
  116. MODULE  struct CIA*           CIAPtr              = (struct CIA *) 0xBFE001;
  117. MODULE    struct RDArgs*        ArgsPtr             = NULL;
  118. MODULE    struct FileRequester* ASLRqPtr            = NULL;
  119. MODULE    struct IOAudio*       AudioRqPtr[4]       = {NULL, NULL, NULL, NULL};
  120. MODULE    struct TextFont*      FontPtr             = NULL;
  121. MODULE    struct Process*       ProcessPtr          = NULL;
  122. MODULE    struct MMD0*          SongPtr             = NULL;
  123. MODULE    struct WBArg*         WBArg               = NULL;
  124. MODULE    struct WBStartup*     WBMsg               = NULL;
  125.  
  126. /* FUNCTIONS -------------------------------------------------------------- */
  127.  
  128. int main(int argc, char** argv)
  129. {
  130. BPTR    OldDir;
  131. SLONG   i;
  132. SBYTE   error = 0,
  133.         player, which;
  134. TEXT    saystring[SAYLIMIT + 1];
  135. SWORD   oldsecondsleft;
  136. UWORD   Pens[10] =
  137. {       BLACK,          /* DETAILPEN            text in title bar */
  138.         WHITE,          /* BLOCKPEN             fill title bar */
  139.         WHITE,          /* TEXTPEN              regular text on BACKGROUNDPEN */
  140.         LIGHTGREY,      /* SHINEPEN             bright edge */
  141.         DARKGREY,       /* SHADOWPEN            dark edge */
  142.         PURPLE,         /* FILLPEN              filling active window borders
  143.                                                 and selected gadgets */
  144.         BLACK,          /* FILLTEXTPEN          text rendered over FILLPEN */
  145.         BLACK,          /* BACKGROUNDPEN        background colour */
  146.         RED,            /* HIGHLIGHTTEXTPEN     highlighted text on BACKGROUNDPEN */
  147.         (UWORD) ~0      /* and used against BLOCKPEN in ASL save requesters */
  148. };
  149. SLONG                   args[12] = {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L};
  150. struct ColorSpec        Colours[21] =
  151. {   /* colour   red   green   blue   description */
  152.     {   0,      0x0,    0x0,    0x0},   /*        BLACK */
  153.     {   1,      0xF,    0xF,    0xF},   /*        WHITE */
  154.     {   2,      0x5,    0x5,    0x5},   /*    DARKGREY */
  155.     {   3,      0x9,    0x9,    0x9},   /*  MEDIUMGREY */
  156.     {   4,      0xB,    0xB,    0xB},   /*   LIGHTGREY */
  157.     {   5,      0x0,    0x0,    0x0},   /*        black */
  158.     {   6,      0xC,    0x5,    0xC},   /*        PURPLE */
  159.     {   7,      0x7,    0x7,    0x2},   /*        brown */
  160.     {   8,      0x3,    0x9,    0x3},   /*    DARKGREEN */
  161.     {   9,      0x4,    0xE,    0x4},   /*  light GREEN */
  162.     {   10,     0xF,    0x1,    0x1},   /*    DARKRED */
  163.     {   11,     0xF,    0x5,    0x5},   /*  light RED */
  164.     {   12,     0x3,    0x3,    0xF},   /*    DARKBLUE */
  165.     {   13,     0x6,    0x6,    0xF},   /*  light BLUE */
  166.     {   14,     0xA,    0xA,    0x2},   /*    DARKYELLOW */
  167.     {   15,     0xC,    0xC,    0x2},   /*  light YELLOW */
  168.     {   16,     0x0,    0x0,    0x0},   /* pointer: transparent */
  169.     {   17,     0xE,    0x4,    0x4},   /* pointer: fill */
  170.     {   18,     0x3,    0x3,    0x3},   /* pointer: shadow */
  171.     {   19,     0xC,    0xC,    0xC},   /* pointer: shine */
  172.     {   -1,     NULL,   NULL,   NULL}
  173. };
  174.  
  175. /* Start of program.
  176.  
  177. version embedding into executable */
  178.  
  179. if (0) /* that is, never */
  180.     say(VERSION, ANYTHING);
  181.  
  182. if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37L)))
  183. {    Write(Output(), OLDKICKSTART, strlen(OLDKICKSTART));
  184.     cleanexit(EXIT_FAILURE);
  185. }
  186.  
  187. /* From this point onwards, we can be sure we have Kickstart 2.04+... */
  188.  
  189. for (i = 0; i <= SAMPLES; i++)
  190.     samp[i].base = NULL;
  191. enginesetup();
  192. ProcessPtr = (struct Process *) FindTask(NULL);
  193.  
  194. if (SysBase->LibNode.lib_Version < 36L)
  195. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Need exec.library V36+!\0", 24);
  196.     cleanexit(EXIT_FAILURE);
  197. }
  198. if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0L)))
  199. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open graphics.library!\0", 24);
  200.     cleanexit(EXIT_FAILURE);
  201. }
  202. if (!(GadToolsBase = (struct GadToolsBase *) OpenLibrary("gadtools.library", 37L)))
  203. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open GadTools.library V37+!\0", 24);
  204.     cleanexit(EXIT_FAILURE);
  205. }
  206. if (!(ASLBase = (struct ASLBase *) OpenLibrary("asl.library", 0L)))
  207. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open ASL.library!\0", 24);
  208.     cleanexit(EXIT_FAILURE);
  209. }
  210. if (!(UtilityBase = (struct UtilityBase *) OpenLibrary("utility.library", 0L)))
  211. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open utility.library!\0", 24);
  212.     cleanexit(EXIT_FAILURE);
  213. }
  214. if (!(IconBase = (struct Library *) OpenLibrary("icon.library", 0L)))
  215. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open icon.library!\0", 24);
  216.         cleanexit(EXIT_FAILURE);
  217. }
  218. LowLevelBase = (struct Library *) OpenLibrary("lowlevel.library", 0L);
  219.  
  220. /* argument parsing */
  221.  
  222. if (argc) /* started from CLI */
  223. {   if (!(ArgsPtr = ReadArgs
  224.     (   "-N=NOPRELOAD/S,-A=NOANIMS/S,-I=NOICONS/S,-P=PRI/K/N,-O=OVERHEAD/S,"
  225.         "-S=SHUFFLE/S,-Q=QUIET/S,GREEN/K,RED/K,BLUE/K,YELLOW/K,FILE",
  226.         (LONG *) args,
  227.         NULL
  228.     )))
  229.     {   Printf
  230.         (   "Usage: %s [-n=NOPRELOAD] [-a=NOANIMS] [-i=NOICONS] "
  231.             "[-p=PRI <priority>] [-o=OVERHEAD] [-s=SHUFFLE] [-q=QUIET] "
  232.             "[GREEN=KYBD|JOY|GAMEPAD|AMIGA|NONE] "
  233.             "[RED=KYBD|JOY|GAMEPAD|AMIGA|NONE] [BLUE=JOY|GAMEPAD|AMIGA|NONE] "
  234.             "[YELLOW=JOY|GAMEPAD|AMIGA|NONE] [[FILE=]<fieldset>]\n",
  235.             argv[0]
  236.         );
  237.         cleanexit(EXIT_FAILURE);
  238.     }
  239.     if (args[0])
  240.     {   fxable = musicable = DEFER;
  241.     }
  242.     if (args[1])
  243.     {   anims = FALSE;
  244.     }
  245.     if (args[2])
  246.     {   icons = FALSE;
  247.     }
  248.     if (args[3])
  249.     {   if (args[3] < -128 || args[3] > 5)
  250.         {   Printf("%s: Priority range is -128 to +5\n", argv[0]);
  251.             cleanexit(EXIT_FAILURE);
  252.         }
  253.         OldPri = SetTaskPri((struct Task *) ProcessPtr, args[3]);
  254.     }
  255.     if (args[4])
  256.     {   iso = FALSE;
  257.     }
  258.     if (args[5])
  259.     {   randomflag = TRUE;
  260.     }
  261.     if (args[6])
  262.     {   quiet = TRUE;
  263.     }
  264.     if (args[7])
  265.     {   if (!stricmp(args[7], "KYBD"))
  266.         {   worm[0].control = KEYBOARD;
  267.         } elif (!stricmp(args[7], "JOY"))
  268.         {   worm[0].control = JOYSTICK;
  269.         } elif (!stricmp(args[7], "GAMEPAD"))
  270.         {   if (!LowLevelBase)
  271.             {   Printf("%s: Can't open lowlevel.library!\n", argv[0]);
  272.                 cleanexit(EXIT_FAILURE);
  273.             }
  274.             worm[0].control = GAMEPAD;
  275.         } elif (!stricmp(args[7], "AMIGA"))
  276.         {   worm[0].control = AMIGA;
  277.         } elif (!stricmp(args[7], "NONE"))
  278.         {   worm[0].control = NONE;
  279.         } else
  280.         {   Printf("%s: Green worm control must be KYBD, JOY, GAMEPAD, AMIGA or NONE\n", argv[0]);
  281.             cleanexit(EXIT_FAILURE);
  282.     }   }
  283.     if (args[8])
  284.     {   if (!stricmp(args[8], "KYBD"))
  285.         {   worm[1].control = KEYBOARD;
  286.         } elif (!stricmp(args[8], "JOY"))
  287.         {   worm[1].control = JOYSTICK;
  288.         } elif (!stricmp(args[8], "GAMEPAD"))
  289.         {   if (!LowLevelBase)
  290.             {   Printf("%s: Can't open lowlevel.library!\n", argv[0]);
  291.                 cleanexit(EXIT_FAILURE);
  292.             }
  293.             worm[1].control = GAMEPAD;
  294.         } elif (!stricmp(args[8], "AMIGA"))
  295.         {   worm[1].control = AMIGA;
  296.         } elif (!stricmp(args[8], "NONE"))
  297.         {   worm[1].control = NONE;
  298.         } else
  299.         {   Printf("%s: Red worm control must be KYBD, JOY, GAMEPAD, AMIGA or NONE\n", argv[0]);
  300.             cleanexit(EXIT_FAILURE);
  301.     }   }
  302.     if (args[9])
  303.     {   if (!stricmp(args[9], "JOY"))
  304.         {   worm[2].control = JOYSTICK;
  305.         } elif (!stricmp(args[9], "GAMEPAD"))
  306.         {   if (!LowLevelBase)
  307.             {   Printf("%s: Can't open lowlevel.library!\n", argv[0]);
  308.                 cleanexit(EXIT_FAILURE);
  309.             }
  310.             worm[2].control = GAMEPAD;
  311.         } elif (!stricmp(args[9], "AMIGA"))
  312.         {   worm[2].control = AMIGA;
  313.         } elif (!stricmp(args[9], "NONE"))
  314.         {   worm[2].control = NONE;
  315.         } else
  316.         {   Printf("%s: Blue worm control must be JOY, GAMEPAD, AMIGA or NONE\n", argv[0]);
  317.             cleanexit(EXIT_FAILURE);
  318.     }   }
  319.     if (args[10])
  320.     {   if (!stricmp(args[10], "JOY"))
  321.         {   worm[3].control = JOYSTICK;
  322.         } elif (!stricmp(args[10], "GAMEPAD"))
  323.         {   if (!LowLevelBase)
  324.             {   Printf("%s: Can't open lowlevel.library!\n", argv[0]);
  325.                 cleanexit(EXIT_FAILURE);
  326.             }
  327.             worm[3].control = GAMEPAD;
  328.         } elif (!stricmp(args[10], "AMIGA"))
  329.         {   worm[3].control = AMIGA;
  330.         } elif (!stricmp(args[10], "NONE"))
  331.         {   worm[3].control = NONE;
  332.         } else
  333.         {   Printf("%s: Yellow worm control must be JOY, GAMEPAD, AMIGA or NONE\n", argv[0]);
  334.             cleanexit(EXIT_FAILURE);
  335.     }   }
  336.     if (args[11])
  337.     {   strcpy(pathname, args[11]);
  338. }   }
  339. else /* started from WB */
  340. {   WBMsg = (struct WBStartup *) argv;
  341.     WBArg = WBMsg->sm_ArgList; /* head of the arg list */
  342.  
  343.     for (i = 0;
  344.          i < WBMsg->sm_NumArgs;
  345.          i++, WBArg++)
  346.     {   if (WBArg->wa_Lock)
  347.         {    /* something that does not support locks */
  348.              parsewb();
  349.         }
  350.         else
  351.         {    /* locks supported, change to the proper directory */
  352.              OldDir = CurrentDir(WBArg->wa_Lock);
  353.              parsewb();
  354.              CurrentDir(OldDir);
  355.         }
  356.         if (i == 1)
  357.         {    strcpy(pathname, WBArg->wa_Name);
  358. }   }   }
  359.  
  360. if (!(DiskFontBase = (struct DiskFontBase *) OpenLibrary("diskfont.library", 0L)))
  361.     error = 1;
  362.     
  363. if
  364. (       (!(StartValPtr = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
  365. ||      (!(CurrentValPtr = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
  366. ||      (!(PausedValPtr = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
  367. )
  368. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't allocate timer value structure!\0", 24);
  369.     cleanexit(EXIT_FAILURE);
  370. }
  371. if (!(TimerPortPtr = (struct MsgPort *) CreateMsgPort()))
  372. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't allocate timer message port!\0", 24);
  373.     cleanexit(EXIT_FAILURE);
  374. }
  375. if (!(TimerRqPtr = (struct timerequest *) CreateIORequest(TimerPortPtr, sizeof(struct timerequest))))
  376. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create timer I/O request!\0", 24);
  377.     cleanexit(EXIT_FAILURE);
  378. }
  379. if (TimerClosed = OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *) TimerRqPtr, 0))
  380. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open timer.device!\0", 24);
  381.     cleanexit(EXIT_FAILURE);
  382. }
  383. TimerBase = (struct Library *) TimerRqPtr->tr_node.io_Device;
  384.  
  385. /* PREPARE DISPLAY -----------------------------------------------
  386.  
  387. font and screen */
  388.  
  389. if ((!error) && (FontPtr = (struct TextFont *) OpenDiskFont(&WormWars8)))
  390. {   /* loaded WormWars.font */
  391.  
  392.     ScreenPtr = (struct Screen *) OpenScreenTags
  393.     (   NULL,
  394.         SA_Width,       640,
  395.         SA_Height,      256,
  396.         SA_Depth,       DEPTH,
  397.         SA_DisplayID,   HIRES_KEY | PAL_MONITOR_ID,
  398.         SA_Title,       TITLEBAR,
  399.         SA_Colors,      Colours,
  400.         SA_Font,        &WormWars8,
  401.         SA_Pens,        Pens,
  402.         SA_PubName,     "WORMWARS",
  403.         TAG_DONE
  404.     );
  405.     if (!ScreenPtr)
  406.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open screen!\0", 24);
  407.         cleanexit(EXIT_FAILURE);
  408. }   }
  409. else
  410. {   if (!error)
  411.         ; // error = 2;
  412.  
  413.     ScreenPtr = (struct Screen *) OpenScreenTags
  414.     (   NULL,
  415.         SA_Width,       640,
  416.         SA_Height,      256,
  417.         SA_Depth,       DEPTH,
  418.         SA_DisplayID,   HIRES_KEY | PAL_MONITOR_ID,
  419.         SA_Title,       TITLEBAR,
  420.         SA_Colors,      Colours,
  421.         SA_Font,        &Topaz8,
  422.         SA_Pens,        Pens,
  423.         SA_PubName,     "WORMWARS",
  424.         TAG_DONE
  425.     );
  426.     if (!ScreenPtr)
  427.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open screen nor font!\0", 24);
  428.         cleanexit(EXIT_FAILURE);
  429. }   }
  430.  
  431.     PubScreenStatus(ScreenPtr, NULL); // take the screen public
  432.  
  433.     /* For some reason, we lose 4 memory chunks around this point: 2 chunks
  434.     each of 292 bytes and 10 bytes. It is related to OpenScreenTagList(),
  435.     but is not related to the SA_Colors, SA_Font or SA_Pens tags, nor to
  436.     the timer.device.
  437.  
  438.     Now that the screen is set up, we initialize the turbo graphics as
  439.     follows: */
  440.  
  441.     for (i = 0; i < DEPTH; i++)
  442.         QDPI.pi_Planes[i] = ScreenPtr->RastPort.BitMap->Planes[i];
  443.     QDPI.pi_BPR           = ScreenPtr->RastPort.BitMap->BytesPerRow;
  444.     QDPI.pi_PlaneSZ       = ScreenPtr->RastPort.BitMap->BytesPerRow *
  445.                             ScreenPtr->RastPort.BitMap->Rows;
  446.  
  447.     /* GetBitMapAttr() is more forwards-compatible than direct structure
  448.     access. */
  449.     useqdraw = (SysBase->AttnFlags & AFB_68020);
  450.     if (!useqdraw)
  451.     {   anims = FALSE;
  452.     }
  453.  
  454.     // These must be done before the menus are set up
  455.     if (anims)
  456.     {   NewMenu[INDEX_ANIMATIONS].nm_Flags |= CHECKED;
  457.     }
  458.     if (icons)
  459.     {   NewMenu[INDEX_CREATEICONS].nm_Flags |= CHECKED;
  460.     }
  461.  
  462.     /* GadTools */
  463.     if (!(CycleGadget.ng_VisualInfo = StringGadget.ng_VisualInfo = CheckboxGadget.ng_VisualInfo = RandomGadget.ng_VisualInfo = VisualInfoPtr = (APTR) GetVisualInfo(ScreenPtr, TAG_DONE)))
  464.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't get GadTools visual info!\0", 24);
  465.     cleanexit(EXIT_FAILURE);
  466.     }
  467.     if (!(MenuPtr = (struct Menu *) CreateMenus(NewMenu, TAG_DONE)))
  468.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create menus!\0", 24);
  469.         cleanexit(EXIT_FAILURE);
  470.     }
  471.     if (!(LayoutMenus(MenuPtr, VisualInfoPtr, TAG_DONE)))
  472.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't lay out menus!\0", 24);
  473.     cleanexit(EXIT_FAILURE);
  474.     }
  475.     if (!(PrevGadgetPtr = (struct Gadget *) CreateContext(&GListPtr)))
  476.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create GadTools context!\0", 24);
  477.     cleanexit(EXIT_FAILURE);
  478.     }
  479.  
  480.     for (player = 0; player <= 3; player++)
  481.     {   CycleGadget.ng_TopEdge = TSOFFSET + 84 + (player * (FONTX + 8));
  482.     CycleGadget.ng_GadgetText = CycleText[player];
  483.     CycleGadgetPtr[player] = PrevGadgetPtr = (struct Gadget *) CreateGadget
  484.         (   CYCLE_KIND,
  485.             PrevGadgetPtr,
  486.             &CycleGadget,
  487.             GTCY_Labels, CycleOptions[player],
  488.             GTCY_Active, worm[player].control,
  489.             GT_Underscore, '_',
  490.             GA_Disabled, TRUE,
  491.             TAG_DONE
  492.     );
  493.     }
  494.     RandomGadgetPtr = PrevGadgetPtr = (struct Gadget *) CreateGadget
  495.     (   CHECKBOX_KIND,
  496.     PrevGadgetPtr,
  497.     &RandomGadget,
  498.     GTCB_Checked, randomflag,
  499.     GT_Underscore, '_',
  500.     GA_Disabled, TRUE,
  501.     TAG_DONE
  502.     );
  503.     CheckboxGadgetPtr = PrevGadgetPtr = (struct Gadget *) CreateGadget
  504.     (   CHECKBOX_KIND,
  505.     PrevGadgetPtr,
  506.     &CheckboxGadget,
  507.     GTCB_Checked, iso,
  508.     GT_Underscore, '_',
  509.     GA_Disabled, TRUE,
  510.     TAG_DONE
  511.     );
  512.  
  513.     /* main window */
  514.     if (!(MainWindowPtr = (struct Window *) OpenWindowTags(NULL,
  515.         WA_Top,                 11,
  516.         WA_Width,               SCREENXPIXEL,
  517.         WA_Height,              SCREENYPIXEL,
  518.         WA_IDCMP,               IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS | IDCMP_CLOSEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_MENUPICK | IDCMP_MENUVERIFY | CYCLEIDCMP | STRINGIDCMP | CHECKBOXIDCMP | IDCMP_REFRESHWINDOW | IDCMP_INTUITICKS,
  519.         WA_Gadgets,             GListPtr,
  520.     WA_CustomScreen,    ScreenPtr,
  521.     WA_Borderless,        TRUE,
  522.     WA_Activate,        TRUE,
  523.     WA_SmartRefresh,    TRUE,
  524.         WA_ReportMouse,         TRUE,
  525.     WA_RptQueue,        16,
  526.     TAG_DONE)))
  527.         {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open main window!\0", 24);
  528.             cleanexit(EXIT_FAILURE);
  529.     }
  530.    
  531.     /* redirection of AmigaDOS system requesters */
  532.     
  533.     OldWindowPtr = ProcessPtr->pr_WindowPtr;
  534.     ProcessPtr->pr_WindowPtr = (APTR) MainWindowPtr;
  535.  
  536.     if (!(ASLRqPtr = AllocAslRequestTags(ASL_FileRequest, ASL_Pattern, PATTERN, ASL_Window, MainWindowPtr, TAG_DONE)))
  537.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create ASL request!\0", 24);
  538.         cleanexit(EXIT_FAILURE);
  539.     }
  540.  
  541.     /* String gadgets: first the window is opened with the cycle and
  542.     checkbox gadgets.
  543.       It is necessary not to display the string gadgets yet, so the
  544.     gadgets are then created and added to the gadget list. You will note
  545.     there is no command given to render them as yet. When the attributes
  546.     are modified at highscore time, the applicable gadget is refreshed
  547.     then. */
  548.  
  549.     for (which = 0; which <= HISCORES; which++)
  550.     {   StringGadget.ng_TopEdge = TSOFFSET + (which * HISCOREDISTANCE);
  551.         StringGadgetPtr[which] = PrevGadgetPtr = (struct Gadget *) CreateGadget(STRING_KIND, PrevGadgetPtr, &StringGadget, GTST_MaxChars, NAMELENGTH, STRINGA_ReplaceMode, TRUE, GA_Disabled, TRUE, TAG_DONE);
  552.     }
  553.     if (!PrevGadgetPtr)
  554.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create GadTools gadgets!\0", 24);
  555.     cleanexit(EXIT_FAILURE);
  556.     }
  557.  
  558.     if (musicable == UNTRIED)
  559.         loadthemusic();
  560.     if (fxable == UNTRIED)
  561.         loadthefx();
  562.     if (fxable == SUCCEEDED)
  563.         toggle(F);
  564.     else if (musicable == SUCCEEDED)
  565.         toggle(M);
  566.  
  567.     strcpy(saystring, "Loading ");
  568.     strcat(saystring, pathname);
  569.     strcat(saystring, "...");
  570.     say(saystring, WHITE);
  571.     if (loadfields(pathname))
  572.     {   if (!quiet)
  573.         {   strcpy(saystring, "Can't open ");
  574.             strcat(saystring, pathname);
  575.             strcat(saystring, "!");
  576.             say(saystring, RED);
  577.         }
  578.         newfields();
  579.         if (!quiet)
  580.         {   anykey(TRUE);
  581.     }   }
  582.  
  583.     while (1)
  584.     {   titlescreen();
  585.         for (i = 0; i <= 3; i++)
  586.         {   if (worm[i].control == GAMEPAD)
  587.             {   SetJoyPortAttrs(worm[i].port, SJA_Type, SJA_TYPE_GAMECTLR, TAG_DONE);
  588.         }   }
  589.     
  590.         /* MAIN GAME LOOP ------------------------------------------------- */
  591.     
  592.         while (a == PLAYGAME)
  593.         {   r++;
  594.             oldsecondsleft = secondsleft;
  595.             GetSysTime(CurrentValPtr);
  596.             SubTime(CurrentValPtr, StartValPtr);
  597.             secondsleft = secondsperlevel - CurrentValPtr->tv_secs;
  598.             if (secondsleft != oldsecondsleft)
  599.                 timeloop();
  600.             if (!turbo)
  601.             {   TimerRqPtr->tr_node.io_Command  = TR_ADDREQUEST;
  602.                 TimerRqPtr->tr_time.tv_secs     = 0;
  603.                 TimerRqPtr->tr_time.tv_micro    = delay;
  604.                 SendIO((struct IORequest *) TimerRqPtr);
  605.             }
  606.             gameloop();
  607.             // CheckIO() returns FALSE if complete, pointer if incomplete.
  608.             if (CheckIO((struct IORequest *) TimerRqPtr)) // CheckIO returns pointer to struct IORequest
  609.                 draw(CLOCKICON, ICONY, CLOCK);
  610.             else draw(CLOCKICON, ICONY, BLACKENED);
  611.             WaitIO((struct IORequest *) TimerRqPtr);
  612.         }
  613.         say("Title Screen", WHITE);
  614. }   }
  615.     
  616. /* SUPPORT FUNCTIONS ----------------------------------------------------- */
  617.     
  618. /* NAME     anykey -- wait for a user press
  619. SYNOPSIS    anykey(ABOOL);
  620. FUNCTION    Waits for a user response. Optional automatic timeout.
  621. INPUTS      timeout - timeout?
  622. RESULTS     FALSE if user presses Escape, TRUE otherwise.
  623. FILE        system.c */
  624.  
  625. ABOOL anykey(ABOOL timeout)
  626. {   ABOOL                done       = FALSE;
  627.     SBYTE                count      = 0;
  628.     UWORD                code, qual;
  629.     ULONG                class;
  630.     struct IntuiMessage* MsgPtr;
  631.  
  632.     clearkybd();
  633.     Forbid();
  634.     MainWindowPtr->Flags |= WFLG_RMBTRAP;
  635.     Permit();
  636.     
  637.         while (!done && !firebutton())
  638.         {   while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  639.             {   class = MsgPtr->Class;
  640.                 code  = MsgPtr->Code;
  641.                 qual  = MsgPtr->Qualifier;
  642.                 GT_ReplyIMsg(MsgPtr);
  643.                 switch(class)
  644.                 {
  645.                 case IDCMP_RAWKEY:
  646.                     if ((!(qual & IEQUALIFIER_REPEAT)) && code < KEYUP && (code < FIRSTQUALIFIER || code > LASTQUALIFIER))
  647.                     {   done = TRUE;
  648.                         if (code == M)
  649.                             toggle(M);
  650.                         elif (code == F)
  651.                             toggle(F);
  652.                         elif (code == ESCAPE)
  653.                         {   if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  654.                             {   if (verify())
  655.                                 {   cleanexit(EXIT_SUCCESS);
  656.                                 } else
  657.                                 {   Forbid();
  658.                                     MainWindowPtr->Flags &= ~WFLG_RMBTRAP;
  659.                                     Permit();
  660.                                     return(FALSE);
  661.                     }   }   }   }
  662.                 break;
  663.                 case IDCMP_CLOSEWINDOW:
  664.                     cleanexit(EXIT_SUCCESS);
  665.                 break;
  666.                 case IDCMP_ACTIVEWINDOW:
  667.                     ignore = TRUE;
  668.                 break;
  669.                 case IDCMP_MOUSEBUTTONS:
  670.                     if ((code == SELECTDOWN || code == MENUDOWN) && !(qual & IEQUALIFIER_REPEAT))
  671.                         if (ignore)
  672.                             ignore = FALSE;
  673.                         else done = TRUE;
  674.                 break;
  675.                 case IDCMP_INTUITICKS:
  676.                     if (timeout && ++count > PATIENCE)
  677.                         done = TRUE;
  678.                 break;
  679.                 default:
  680.                 break;
  681.         }   }   }
  682.  
  683.     Forbid();
  684.     MainWindowPtr->Flags &= ~WFLG_RMBTRAP;
  685.     Permit();
  686.         return TRUE;
  687. }
  688.  
  689. void celebrate(void)
  690. {   ABOOL                done = FALSE;
  691.     ULONG                class;
  692.     UWORD                code, qual;
  693.     struct IntuiMessage* MsgPtr;
  694.     UBYTE                player;
  695.  
  696.     for (player = 0; player <= 3; player++)
  697.         if (worm[player].control != NONE && worm[player].score >= worm[player].hiscore)
  698.              worm[player].hiscore = worm[player].score;
  699.     waitasec();
  700.     clearkybd();
  701.     while (!done && !firebutton)
  702.     {   while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  703.         {   class = MsgPtr->Class;
  704.             code  = MsgPtr->Code;
  705.             qual  = MsgPtr->Qualifier;
  706.             GT_ReplyIMsg(MsgPtr);
  707.             switch (class)
  708.             {
  709.             case IDCMP_RAWKEY:
  710.                 if (code == ESCAPE)
  711.                 {   if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  712.                     {   if (verify())
  713.                         {   cleanexit(EXIT_SUCCESS);
  714.                     }   }
  715.                     else
  716.                     {   done = TRUE;
  717.                 }   }
  718.                 elif (code == RETURN || code == ENTER || code == SPACEBAR)
  719.                 {   done = TRUE;
  720.                 } elif (code == M)
  721.                 {   toggle(M);
  722.                 } elif (code == F)
  723.                 {   toggle(F);
  724.                 }
  725.             break;
  726.             case IDCMP_MOUSEBUTTONS:
  727.                 if (code == SELECTDOWN && !(qual & IEQUALIFIER_REPEAT))
  728.                 {   if (ignore)
  729.                     {   ignore = FALSE;
  730.                 }   }
  731.                 else
  732.                 {   done = TRUE;
  733.                 }
  734.             break;
  735.             case IDCMP_ACTIVEWINDOW:
  736.                 ignore = TRUE;
  737.             break;
  738.             case IDCMP_CLOSEWINDOW:
  739.                 cleanexit(EXIT_SUCCESS);
  740.             break;
  741.             default:
  742.             break;
  743.         }   }
  744.         SetRGB4(&ScreenPtr->ViewPort, 17, rand() % 16, rand() % 16, rand() % 16);
  745.         draw(rand() % (FIELDX + 1), rand() % (FIELDY + 1), rand() % LASTOBJECT);
  746.     }
  747.     SetRGB4(&ScreenPtr->ViewPort, 17, 14, 4, 4);
  748.     a = GAMEOVER;
  749. }
  750.  
  751. void cleanexit(SLONG rc)
  752. {   SBYTE i;
  753.  
  754. // CheckIO() returns NULL if complete, pointer if incomplete?
  755. if (TimerRqPtr && (!CheckIO((struct IORequest *) TimerRqPtr)))
  756.                         {       AbortIO((struct IORequest *) TimerRqPtr);
  757.                                 WaitIO((struct IORequest *) TimerRqPtr);
  758.                         }
  759.                                 freefx();
  760.                                 for (i = 0; i <= SAMPLES; i++)
  761.                                     if (samp[i].base)
  762.                                         FreeMem(samp[i].base, samp[i].size);
  763. if (mode == MUSIC)        StopPlayer();
  764. if (SongPtr)            UnLoadModule(SongPtr);
  765. if (MEDPlayerBase)    {    FreePlayer();
  766.                                 CloseLibrary((struct Library *) MEDPlayerBase);
  767.                         }
  768.  
  769. if (ASLRqPtr)            FreeAslRequest(ASLRqPtr);
  770. if (OldWindowPtr)        ProcessPtr->pr_WindowPtr = OldWindowPtr;
  771. if (MainWindowPtr)    {    clearkybd();
  772.                                 ClearMenuStrip(MainWindowPtr);
  773.                                 CloseWindow(MainWindowPtr);                                             }
  774. if (GListPtr)            FreeGadgets(GListPtr);
  775. if (MenuPtr)            FreeMenus(MenuPtr);
  776. if (VisualInfoPtr)        FreeVisualInfo(VisualInfoPtr);
  777. if (ScreenPtr)            CloseScreen(ScreenPtr);
  778. if (FontPtr)            CloseFont(FontPtr);
  779. if (!TimerClosed)               CloseDevice((struct IORequest *) TimerRqPtr);
  780. if (TimerRqPtr)                 DeleteIORequest(TimerRqPtr);
  781. if (TimerPortPtr)        DeleteMsgPort(TimerPortPtr);
  782. if (PausedValPtr)        FreeMem(PausedValPtr, sizeof(struct timeval));
  783. if (CurrentValPtr)        FreeMem(CurrentValPtr, sizeof(struct timeval));
  784. if (StartValPtr)        FreeMem(StartValPtr, sizeof(struct timeval));
  785. if (DiskFontBase)        CloseLibrary((struct Library *) DiskFontBase);
  786. if (LowLevelBase)
  787. {   for (i = 0; i <= 3; i++)
  788.     {   SetJoyPortAttrs(worm[i].port, SJA_Reinitialize, TRUE, TAG_DONE);
  789.     }
  790.     CloseLibrary((struct Library *) LowLevelBase);
  791. }
  792. if (UtilityBase)                CloseLibrary((struct Library *) UtilityBase);
  793. if (ASLBase)            CloseLibrary((struct Library *) ASLBase);
  794. if (GadToolsBase)        CloseLibrary((struct Library *) GadToolsBase);
  795. if (GfxBase)            CloseLibrary((struct Library *) GfxBase);
  796. if (IconBase)            CloseLibrary((struct Library *) IconBase);
  797. if (IntuitionBase)    {    OpenWorkBench();
  798.                                 CloseLibrary((struct Library *) IntuitionBase);                                    }
  799.                                 SetTaskPri((struct Task *) ProcessPtr, OldPri);
  800. if (ArgsPtr)            FreeArgs(ArgsPtr);
  801.                                 exit(rc); /* End of program. */
  802. }
  803.  
  804. void clearkybd(void)
  805. {   struct IntuiMessage* MsgPtr;
  806.  
  807.     while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  808.         GT_ReplyIMsg(MsgPtr);
  809. }
  810.  
  811. void draw(SBYTE x, SBYTE y, UBYTE image)
  812. {   SWORD pixx;
  813.  
  814.     /* Isometric methodology:
  815.     
  816.     read the colour of the pixel, then set the colour in the image
  817.     to the same value.
  818.  
  819.     So if, for the top left pixel, we read colour 10 (= $A or %1010),
  820.     we have to clear the relevant bits in plane 0 and 2, and set them
  821.     in planes 1 and 3.
  822.  
  823.     plane 0 (least significant bit)
  824.         1234567890123456 pixels for top row (y = 0)
  825.         1234567890123456 pixels for next row (y = 1)
  826.         : : :
  827.     plane 1
  828.         : : :
  829.  
  830.      01234567890
  831.     0..#########
  832.     1..#########
  833.     2.#########.
  834.     3.#########.
  835.     4#########..
  836.     5#########..
  837.  
  838.     Image is effectively 9*6, but requires an 11*6 bitmap. */
  839.  
  840.     if (a == PLAYGAME && iso)
  841.     {   pixx = (x * ISOSQUAREX) + 20 + ((FIELDY - y) * 3);
  842.         pixy = (y * ISOSQUAREY) + STARTYPIXEL;
  843.  
  844.         if (useqdraw)
  845.         {   // Turbo field rendering routines, thanks to Jilles Tjoelker.
  846.             qdraw020iso
  847.             (   &QDPI,
  848.                 pixx,
  849.                 pixy + MainWindowPtr->TopEdge,
  850.                 (WORD *) IsoImageData[image]
  851.             );
  852.         }
  853.         else
  854.         {   IsoImage.ImageData = IsoImageData[image];
  855.             preserve(pixx,      0, image, 32768);
  856.             preserve(pixx,      1, image, 32768);
  857.             preserve(pixx,      2, image, 32768);
  858.             preserve(pixx,      3, image, 32768);
  859.             preserve(pixx +  1, 0, image, 16384);
  860.             preserve(pixx +  1, 1, image, 16384);
  861.             preserve(pixx +  9, 4, image,    64);
  862.             preserve(pixx +  9, 5, image,    64);
  863.             preserve(pixx + 10, 2, image,    32);
  864.             preserve(pixx + 10, 3, image,    32);
  865.             preserve(pixx + 10, 4, image,    32);
  866.             preserve(pixx + 10, 5, image,    32);
  867.             DrawImage
  868.             (   MainWindowPtr->RPort,
  869.                 &IsoImage,
  870.                 pixx,
  871.                 pixy
  872.             );
  873.     }   }
  874.     else
  875.     {   if (useqdraw)
  876.         {   // Turbo field rendering routines, thanks to Jilles Tjoelker.
  877.             qdraw020
  878.             (   &QDPI,
  879.                 STARTXPIXEL + (SQUAREX * x),
  880.                 STARTYPIXEL + (SQUAREY * y) + MainWindowPtr->TopEdge,
  881.                 (WORD *) ImageData[image]
  882.             );
  883.         }
  884.         else
  885.         {   Image.ImageData = ImageData[image];
  886.             DrawImage
  887.             (   MainWindowPtr->RPort,
  888.                 &Image,
  889.                 STARTXPIXEL + (SQUAREX * x),
  890.                 STARTYPIXEL + (SQUAREY * y)
  891.             );
  892. }   }   }
  893.  
  894. MODULE void preserve(SWORD x, SBYTE row, SBYTE image, UWORD amount)
  895. {    LONG colour;
  896.     
  897.     /* x = image top-left corner coordinate on destination screen.
  898.     row = y-row of the image.
  899.     image = image number
  900.     amount = value of the bit to be set/cleared. */
  901.  
  902.     colour = ReadPixel
  903.     (    MainWindowPtr->RPort,
  904.         x,
  905.         pixy + row
  906.     );
  907.     if (colour & 8)
  908.         IsoImageData[image][row + 18] |= amount;
  909.     else IsoImageData[image][row + 18] &= ~amount;
  910.     if (colour & 4)
  911.         IsoImageData[image][row + 12] |= amount;
  912.     else IsoImageData[image][row + 12] &= ~amount;
  913.     if (colour & 2)
  914.         IsoImageData[image][row + 6] |= amount;
  915.     else IsoImageData[image][row + 6] &= ~amount;
  916.     if (colour & 1)
  917.         IsoImageData[image][row] |= amount;
  918.     else IsoImageData[image][row] &= ~amount;
  919. }
  920.  
  921. void effect(SBYTE index)
  922. {   AUTO    SBYTE i;
  923.     AUTO    SBYTE ok            = -1;
  924.     AUTO    ULONG oldestreceipt = (ULONG) -1L;
  925.     PERSIST ULONG nextreceipt   = 1L;
  926.  
  927.     /* oldestreceipt = temporary variable for ascertaining oldest
  928.                        sound still playing.
  929.     nextreceipt = next unused receipt number (monotonically incrementing). */
  930.  
  931.     if (mode == FX)
  932.     {    for (i = 0; i <= 3; i++)
  933.         {    /* decide on a channel */
  934.  
  935.             if (ok == -1)
  936.             {    if (!eversent[i])
  937.                     ok = i;
  938.                                 // CheckIO() returns NULL if complete, pointer if incomplete.
  939.                                 elif (CheckIO((struct IORequest *) AudioRqPtr[i]))
  940.                 {    WaitIO((struct IORequest *) AudioRqPtr[i]);
  941.                     ok = i;
  942.         }    }    }
  943.         if (ok == -1)
  944.         {   for (i = 0; i <= 3; i++)
  945.                 if (receipter[i] < oldestreceipt)
  946.                 {   ok = i;
  947.                     oldestreceipt = receipter[i];
  948.                 }
  949.             AbortIO((struct IORequest *) AudioRqPtr[ok]);
  950.             WaitIO((struct IORequest *) AudioRqPtr[ok]);
  951.         }
  952.         eversent[ok] = TRUE;
  953.         AudioRqPtr[ok]->ioa_Cycles              = 1;
  954.         AudioRqPtr[ok]->ioa_Request.io_Command  = CMD_WRITE;
  955.         AudioRqPtr[ok]->ioa_Request.io_Flags    = ADIOF_PERVOL;
  956.         AudioRqPtr[ok]->ioa_Request.io_Unit     = (struct Unit *) (1 << ok);
  957.         AudioRqPtr[ok]->ioa_Volume              = samp[index].volume;
  958.         AudioRqPtr[ok]->ioa_Period              = (UWORD) samp[index].speed;
  959.         AudioRqPtr[ok]->ioa_Request.io_Message.mn_ReplyPort
  960.                                                 = AudioPortPtr[ok];
  961.         AudioRqPtr[ok]->ioa_Data                = (UBYTE *) samp[index].base;
  962.         AudioRqPtr[ok]->ioa_Length              = samp[index].length[samp[index].bank];
  963.         BeginIO(AudioRqPtr[ok]);
  964.         receipter[ok] = nextreceipt;
  965. }   }
  966.  
  967. AGLOBAL void help(UBYTE type)
  968. {   TEXT  title[10], tempstring[12];
  969.     SWORD x, y;
  970.     UBYTE i;
  971.  
  972.     effect(FXHELP);
  973.     if (type == ORB)
  974.     {   strcpy(title, "Creatures");
  975.         x = 232;
  976.         y = 28 + ((CREATUREHELPS + 1) * 10);
  977.     } else
  978.     {   strcpy(title, "Objects");
  979.         x = 640;
  980.         y = 28 + ((LASTOBJECT / 2) * 10);
  981.     }
  982.     if (!(HelpWindowPtr = (struct Window *) OpenWindowTags(NULL,
  983.     WA_Left,          (SCREENXPIXEL / 2) - (x / 2),
  984.     WA_Top,           (SCREENYPIXEL / 2) - (y / 2),
  985.     WA_Width,         x,
  986.     WA_Height,        y,
  987.     WA_IDCMP,         IDCMP_CLOSEWINDOW |
  988.                       IDCMP_RAWKEY |
  989.                       IDCMP_MOUSEBUTTONS |
  990.                       IDCMP_INTUITICKS,
  991.     WA_Title,         title,
  992.     WA_Gadgets,       NULL,
  993.     WA_CustomScreen,  ScreenPtr,
  994.     WA_DragBar,       TRUE,
  995.     WA_CloseGadget,   TRUE,
  996.     WA_NoCareRefresh, TRUE,
  997.     WA_Activate,      TRUE,
  998.     TAG_DONE)))
  999.     {   say("Can't open help window!", RED);
  1000.         anykey(TRUE);
  1001.     } else
  1002.     {   SetAPen(HelpWindowPtr->RPort, WHITE);
  1003.         if (type == ORB)
  1004.         {   Move(HelpWindowPtr->RPort,  26 + (14 * 8), 20);
  1005.             Text(HelpWindowPtr->RPort, "Pts   Dmg", 9);
  1006.             Move(HelpWindowPtr->RPort,  26, 22);
  1007.             Draw(HelpWindowPtr->RPort, 212, 22);
  1008.             for (i = 0; i <= CREATUREHELPS; i++)
  1009.             {   Image.ImageData = ImageData[creaturehelp[i].image];
  1010.                 DrawImage
  1011.                 (   HelpWindowPtr->RPort,
  1012.                     &Image,
  1013.                     10,
  1014.                     25 + (i * 10)
  1015.                 );
  1016.                 Move(HelpWindowPtr->RPort, 26           , 30 + (i * 10));
  1017.                 Text(HelpWindowPtr->RPort, creaturehelp[i].desc, strlen(creaturehelp[i].desc));
  1018.  
  1019.                 stcl_d(tempstring, creaturehelp[i].score);
  1020.                 align(tempstring, 3, ' ');
  1021.                 Move(HelpWindowPtr->RPort, 26 + (14 * 8), 30 + (i * 10));
  1022.                 Text(HelpWindowPtr->RPort, tempstring, 3);
  1023.  
  1024.                 stcl_d(tempstring, creaturehelp[i].pain);
  1025.                 Move(HelpWindowPtr->RPort, 26 + (21 * 8), 30 + (i * 10));
  1026.                 Text(HelpWindowPtr->RPort, tempstring, strlen(tempstring));
  1027.         }   }
  1028.         else /* assumes (type == AFFIXER) */
  1029.         {   for (i = 0; i <= LASTOBJECT; i++)
  1030.             {   Image.ImageData = ImageData[i];
  1031.                 if (i <= LASTOBJECT / 2)
  1032.                 {   Move(HelpWindowPtr->RPort, 26, 20 + (i * 10));
  1033.                     DrawImage
  1034.                     (   HelpWindowPtr->RPort,
  1035.                         &Image,
  1036.                         10,
  1037.                         15 + (i * 10)
  1038.                     );
  1039.                 } else
  1040.                 {   Move(HelpWindowPtr->RPort, 354, 10 + ((i - (LASTOBJECT / 2)) * 10));
  1041.                     DrawImage
  1042.                     (   HelpWindowPtr->RPort,
  1043.                         &Image,
  1044.                         338,
  1045.                         5 + ((i - (LASTOBJECT / 2)) * 10)
  1046.                     );
  1047.                 }
  1048.                 Text(HelpWindowPtr->RPort, objectdesc[i], strlen(objectdesc[i]));
  1049.         }   }
  1050.         helploop(type);
  1051. }   }
  1052.  
  1053. AGLOBAL void helpmanual(void)
  1054. {
  1055. /*  struct NewAmigaGuide NAG = {NULL};
  1056.     AMIGAGUIDECONTEXT Handle;
  1057.  
  1058.     if (!(AmigaGuideBase = (struct AmigaGuideBase *) OpenLibrary("amigaguide.library", 0L)))
  1059.     {   say("Can't open amigaguide.library!", RED);
  1060.         anykey(TRUE);
  1061.         return;
  1062.     }
  1063.  
  1064.     // Fill in the NewAmigaGuide structure
  1065.     NAG.nag_Name = "PROGDIR:WormWars.guide";
  1066.     NAG.nag_Node = NULL;
  1067.     /* If you wanted a specific node of the AmigaGuide, you could set
  1068.     nag_Node to, for example, "Edit". */
  1069.     NAG.nag_Screen = ScreenPtr; // or NULL for default public screen
  1070.  
  1071.     // Open the AmigaGuide client
  1072.     if (Handle = OpenAmigaGuide(&NAG, TAG_DONE))
  1073.     {   /* if you opened it on the default public screen, add this line:
  1074.         ScreenToFront(ScreenPtr); */
  1075.         // Close the AmigaGuide client
  1076.         CloseAmigaGuide(Handle);
  1077.     } else
  1078.     {   /* if you want to know the reason, add this line:
  1079.         LONG rc = IoErr(); */
  1080.         DisplayBeep(ScreenPtr);
  1081.     }
  1082.     CloseLibrary((struct Library *) AmigaGuideBase); */
  1083.  
  1084.     if (SystemTagList("MultiView >NIL: WormWars.guide", TAG_DONE) == -1)
  1085.         DisplayBeep(ScreenPtr);
  1086.     else ScreenToFront(ScreenPtr);
  1087. }
  1088.  
  1089. AGLOBAL void helpabout(void)
  1090. {       SBYTE line;
  1091.         SLONG projectval;
  1092.         TEXT  projectstring[7], ks[5] = "    ", wb[5] = "    ";
  1093.         ULONG ksval = SysBase->LibNode.lib_Version,
  1094.               wbval = IconBase->lib_Version;
  1095.  
  1096.         effect(FXHELP);
  1097.     if (!(HelpWindowPtr = (struct Window *) OpenWindowTags(NULL,
  1098.     WA_Left,            (SCREENXPIXEL / 2) - (ABOUTXPIXEL / 2),
  1099.     WA_Top,                (SCREENYPIXEL / 2) - (ABOUTYPIXEL / 2),
  1100.     WA_Width,            ABOUTXPIXEL,
  1101.     WA_Height,            ABOUTYPIXEL,
  1102.     WA_IDCMP,            IDCMP_CLOSEWINDOW | IDCMP_RAWKEY,
  1103.     WA_Title,            "About Worm Wars",
  1104.     WA_Gadgets,            NULL,
  1105.         WA_CustomScreen,  ScreenPtr,
  1106.         WA_DragBar,       TRUE,
  1107.         WA_CloseGadget,   TRUE,
  1108.         WA_NoCareRefresh, TRUE,
  1109.         WA_Activate,      TRUE,
  1110.     TAG_DONE)))
  1111.         {   say("Can't open About... window!", RED);
  1112.             anykey(TRUE);
  1113.     } else
  1114.     {    /* calculate project size */
  1115.         
  1116.         projectval =
  1117.                   10                                 // header
  1118.                 + ((HISCORES + 1) * HISCORESIZE)     // high scores
  1119.                 + ((levels + 1) * (8 + (LEVELSIZE))) // level data
  1120.                 + strlen(VERSION);                   // version string
  1121.  
  1122.                 switch(ksval)
  1123.                 {
  1124.                 case 37:
  1125.                         strcpy(ks, "2.04");
  1126.                 break;
  1127.                 case 38:
  1128.                         strcpy(ks, "2.1 ");
  1129.                 break;
  1130.                 case 39:
  1131.                         strcpy(ks, "3.0 ");
  1132.                 break;
  1133.                 case 40:
  1134.                         strcpy(ks, "3.1 ");
  1135.                 break;
  1136.                 default:
  1137.                         strcpy(ks, "4.0+");
  1138.                 break;
  1139.                 }
  1140.  
  1141.                 switch(wbval)
  1142.                 {
  1143.                 case 37:
  1144.                         strcpy(wb, "2.04");
  1145.                 break;
  1146.                 case 38:
  1147.                         strcpy(wb, "2.1 ");
  1148.                 break;
  1149.                 case 39:
  1150.                         strcpy(wb, "3.0 ");
  1151.                 break;
  1152.                 case 40:
  1153.                 case 41:
  1154.                 case 42:
  1155.                 case 43:
  1156.                     strcpy(wb, "3.1 ");
  1157.                 break;
  1158.                 case 44:
  1159.                     strcpy(wb, "3.5 ");
  1160.                 break;
  1161.                 case 45:
  1162.                     strcpy(wb, "3.9 ");
  1163.                 break;
  1164.                 default:
  1165.                     strcpy(wb, "4.0+");
  1166.                 break;
  1167.                 }
  1168.                 
  1169.         SetAPen(HelpWindowPtr->RPort, worm[rand() % 4].colour);
  1170.         RectFill(HelpWindowPtr->RPort, 8, 13, ABOUTXPIXEL - 11, ABOUTYPIXEL - 6);
  1171.         SetAPen(HelpWindowPtr->RPort, ABOUTSHADOW);
  1172.         Move(HelpWindowPtr->RPort, 7, ABOUTYPIXEL - 5);
  1173.         Draw(HelpWindowPtr->RPort, 7, 12);
  1174.         Draw(HelpWindowPtr->RPort, ABOUTXPIXEL - 10, 12);
  1175.         SetAPen(HelpWindowPtr->RPort, ABOUTSHINE);
  1176.         Draw(HelpWindowPtr->RPort, ABOUTXPIXEL - 10, ABOUTYPIXEL - 5);
  1177.         Draw(HelpWindowPtr->RPort, 8, ABOUTYPIXEL - 5);
  1178.         SetAPen(HelpWindowPtr->RPort, BLACK);
  1179.         SetDrMd(HelpWindowPtr->RPort, JAM1);
  1180.         for (line = 0; line <= ABOUTLINES; line++)
  1181.                 {   Move(HelpWindowPtr->RPort, about[line].x, about[line].y);
  1182.                     Text(HelpWindowPtr->RPort, about[line].text, (SBYTE) strlen(about[line].text));
  1183.         }
  1184.         stcl_d(projectstring, projectval);
  1185.                 align(projectstring, 6, ' ');
  1186.         Move(HelpWindowPtr->RPort, PROJECTX, PROJECTY);
  1187.                 Text(HelpWindowPtr->RPort, projectstring, 6);
  1188.                 Move(HelpWindowPtr->RPort, KICKSTARTX, KICKSTARTY);
  1189.                 Text(HelpWindowPtr->RPort, ks, 4);
  1190.                 Move(HelpWindowPtr->RPort, WORKBENCHX, WORKBENCHY);
  1191.                 Text(HelpWindowPtr->RPort, wb, 4);
  1192.  
  1193.                 DrawBevelBox(HelpWindowPtr->RPort, 18, 21, 47, 22, GT_VisualInfo, VisualInfoPtr, TAG_END);
  1194.         DrawImage(HelpWindowPtr->RPort, &About, 19, 22);
  1195.  
  1196.                 helploop(NOSQUARE);
  1197. }        }
  1198.  
  1199. AGLOBAL void helploop(UBYTE type)
  1200. {   ABOOL                done = FALSE;
  1201.     UBYTE                objx, objy;
  1202.     UWORD                code, qual;
  1203.     SWORD                mousex, mousey;
  1204.     SBYTE                birdframe = -1, birddir = 1;
  1205.     ULONG                class;
  1206.     struct IntuiMessage* MsgPtr;
  1207.  
  1208.     while(!done)
  1209.     {   /* if (type == NOSQUARE)
  1210.             DrawGels();
  1211.         else */ Wait(1L << HelpWindowPtr->UserPort->mp_SigBit);
  1212.         while (MsgPtr = (struct IntuiMessage *) GetMsg(HelpWindowPtr->UserPort))
  1213.         {   class  = MsgPtr->Class;
  1214.             code   = MsgPtr->Code;
  1215.             qual   = MsgPtr->Qualifier;
  1216.             mousex = MsgPtr->MouseX;
  1217.             mousey = MsgPtr->MouseY;
  1218.             ReplyMsg((struct Message *) MsgPtr);
  1219.             switch(class)
  1220.             {
  1221.             case IDCMP_INTUITICKS:
  1222.                 if (type == ORB && anims)
  1223.                 {   birdframe += birddir;
  1224.                     Image.ImageData = ImageData[BIRD + birdframe];
  1225.                     DrawImage
  1226.                     (   HelpWindowPtr->RPort,
  1227.                         &Image,
  1228.                         10,
  1229.                         25
  1230.                     );
  1231.                     if (birdframe == 0)
  1232.                     {   birddir = 1;
  1233.                     } elif (birdframe == 2)
  1234.                     {   birddir = -1;
  1235.                 }   }
  1236.             break;
  1237.             case IDCMP_CLOSEWINDOW:
  1238.                 done = TRUE;
  1239.             break;
  1240.             case IDCMP_RAWKEY:
  1241.                 if (code == SPACEBAR || code == RETURN || code == ENTER || code == HELP)
  1242.                     done = TRUE;
  1243.                 elif (code == ESCAPE)
  1244.                 {   if (qual & IEQUALIFIER_LSHIFT || qual & IEQUALIFIER_RSHIFT)
  1245.                     {   if (verify())
  1246.                         {   CloseWindow(HelpWindowPtr);
  1247.                             cleanexit(EXIT_SUCCESS);
  1248.                    }   }
  1249.                    else done = TRUE;
  1250.                 }
  1251.             break;
  1252.             case IDCMP_MOUSEBUTTONS:
  1253.                 if (a == FIELDEDIT && type == AFFIXER)
  1254.                 {   objx = (mousex - 10) / 328; // which column
  1255.                     objy = (mousey - 13) / 10;  // which row
  1256.                     if ((objx == 0 || objx == 1) && (objy >= 0 && objy <= LASTOBJECT / 2))
  1257.                     {   if (objx)
  1258.                             setbrush(objy + 1 + (LASTOBJECT / 2));
  1259.                         else setbrush(objy);
  1260.                         done = TRUE;
  1261.                 }   }
  1262.             break;
  1263.             default:
  1264.             break;
  1265.     }   }   }
  1266.     /* if (type == NOSQUARE)
  1267.          unboingball(); */
  1268.     CloseWindow(HelpWindowPtr);
  1269.     clearkybd();
  1270. }
  1271.  
  1272. AGLOBAL void filedelete(void)
  1273. {   TEXT newpathname[255], temp1[81];
  1274.  
  1275.     if (AslRequestTags(ASLRqPtr, ASL_Hail, "Delete Fieldset", ASL_FuncFlags, FILF_PATGAD | FILF_SAVE, TAG_DONE) && *(ASLRqPtr->rf_File) != 0)
  1276.     {   strcpy(newpathname, ASLRqPtr->rf_Dir);
  1277.         AddPart(newpathname, ASLRqPtr->rf_File, 254);
  1278.         if (DeleteFile(newpathname))
  1279.         {   strcpy(temp1, "Deleted ");
  1280.             strcat(temp1, newpathname);
  1281.             strcat(temp1, ".");
  1282.         } else
  1283.         {   strcpy(temp1, "Couldn't delete ");
  1284.             strcat(temp1, newpathname);
  1285.             strcat(temp1, "!");
  1286.         }
  1287.         say(temp1, WHITE);
  1288. }   }
  1289.  
  1290. AGLOBAL void fileopen(ABOOL revert)
  1291. {   TEXT temp1[81] = {"Opened "}, temp2[3], newpathname[255];
  1292.  
  1293.     strcpy(newpathname, pathname);
  1294.     if (revert || (AslRequestTags(ASLRqPtr, ASL_Hail, "Open Fieldset", ASL_FuncFlags, FILF_PATGAD, TAG_DONE) && *(ASLRqPtr->rf_File)))
  1295.     {   if (!revert)
  1296.         {   strcpy(newpathname, ASLRqPtr->rf_Dir);
  1297.             AddPart(newpathname, ASLRqPtr->rf_File, 254);
  1298.         }
  1299.         if (!loadfields(newpathname))
  1300.         {   strcpy(pathname, newpathname);
  1301.             strcat(temp1, pathname);
  1302.             strcat(temp1, " (");
  1303.             stci_d(temp2, levels);
  1304.             strcat(temp1, temp2);
  1305.             strcat(temp1, " levels).");
  1306.             say(temp1, WHITE);
  1307.             if (a == FIELDEDIT)
  1308.                 turborender();
  1309.             else hiscores();
  1310.         } else
  1311.         {   strcpy(temp1, "Couldn't open ");
  1312.             strcat(temp1, newpathname);
  1313.             strcat(temp1, "!");
  1314.             say(temp1, WHITE);
  1315.     }   }
  1316.     if (a == GAMEOVER)
  1317.         anykey(TRUE);
  1318. }
  1319.  
  1320. AGLOBAL void filesaveas(ABOOL flag)
  1321. {    ABOOL    cont = TRUE;
  1322.     TEXT    newpathname[255], temp1[SAYLIMIT + 1], temp2[3];
  1323.         struct DiskObject* InfoHandle;
  1324.  
  1325.     /* flag is TRUE for 'save as...', FALSE for 'save'. */
  1326.  
  1327.     strcpy(newpathname, pathname);
  1328.     if (flag)
  1329.         if (AslRequestTags(ASLRqPtr, ASL_Hail, "Save Fieldset", ASL_FuncFlags, FILF_PATGAD | FILF_SAVE, TAG_DONE) && *(ASLRqPtr->rf_File) != 0)
  1330.         {   strcpy(newpathname, ASLRqPtr->rf_Dir);
  1331.             AddPart(newpathname, ASLRqPtr->rf_File, 254);
  1332.         } else cont = FALSE;
  1333.     if (cont)
  1334.     {   matchteleports();
  1335.         strcpy(temp1, "Saving ");
  1336.     strcat(temp1, newpathname);
  1337.     strcat(temp1, "...");
  1338.     say(temp1, WHITE);
  1339.     if (savefields(newpathname))
  1340.         {   strcpy(pathname, newpathname);
  1341.             if (icons)
  1342.             {   InfoHandle = GetDiskObjectNew(DEFAULTSET);
  1343.                 InfoHandle->do_CurrentX = NO_ICON_POSITION;
  1344.                 InfoHandle->do_CurrentY = NO_ICON_POSITION;
  1345.                 if (!PutDiskObject(pathname, InfoHandle))
  1346.                 {   say("Couldn't write .info file!", RED);
  1347.                     anykey(TRUE);
  1348.             }   }
  1349.             strcpy(temp1, "Saved ");
  1350.             strcat(temp1, pathname);
  1351.             strcat(temp1, " (");
  1352.             stci_d(temp2, levels);
  1353.             strcat(temp1, temp2);
  1354.             strcat(temp1, " levels).");
  1355.         } else
  1356.         {   strcpy(temp1, "Couldn't save ");
  1357.             strcat(temp1, newpathname);
  1358.             strcat(temp1, "!");
  1359.         }
  1360.         say(temp1, WHITE);
  1361.         if (a == GAMEOVER)
  1362.             anykey(TRUE);
  1363. }   }
  1364.  
  1365. MODULE void freefx(void)
  1366. {   SBYTE i;
  1367.  
  1368.     stopfx();
  1369.     if (!AudioClosed)
  1370.     {   CloseDevice((struct IORequest *) AudioRqPtr[0]);
  1371.         AudioClosed = TRUE;
  1372.     }
  1373.     for (i = 0; i <= 3; i++)
  1374.     {   if (AudioRqPtr[i])
  1375.         {   DeleteIORequest(AudioRqPtr[i]);
  1376.             AudioRqPtr[i] = NULL;
  1377.         }
  1378.         if (AudioPortPtr[i])
  1379.         {   // if we ReplyMsg() first, that causes crashes
  1380.             DeleteMsgPort(AudioPortPtr[i]);
  1381.             AudioPortPtr[i] = NULL;
  1382.     }   }
  1383.     if (fbase)
  1384.     {   FreeMem(fbase, fsize);
  1385.         fbase = NULL;
  1386.     }
  1387.     if (FilePtr)
  1388.     {   Close(FilePtr);
  1389.         FilePtr = NULL;
  1390. }   }
  1391.  
  1392. void gameinput(void)
  1393. {   ABOOL                allowed = TRUE,
  1394.                          done;
  1395.     UWORD                code, qual;
  1396.     ULONG                class;
  1397.     struct IntuiMessage* MsgPtr;
  1398.     SBYTE                keyplayer, i;
  1399.     UBYTE                which;
  1400.  
  1401.     for (which = 0; which <= NUMKEYS; which++)
  1402.         key[which].down = FALSE;
  1403.  
  1404. /* keyboard */
  1405.  
  1406. while (MsgPtr = (struct IntuiMessage *) GetMsg(MainWindowPtr->UserPort))
  1407. {   class = MsgPtr->Class;
  1408.     code  = MsgPtr->Code;
  1409.     qual  = MsgPtr->Qualifier;
  1410.     ReplyMsg((struct Message *) MsgPtr);
  1411.     if (class == IDCMP_RAWKEY && (!(qual & IEQUALIFIER_REPEAT)))
  1412.     {   if (code < KEYUP)
  1413.         {   switch(code)
  1414.             {
  1415.             case M:
  1416.                 toggle(M);
  1417.             break;
  1418.                 case F:
  1419.                 toggle(F);
  1420.             break;
  1421.             case KEY_T:
  1422.                 // check whether any human worms are playing
  1423.                 for (i = 0; i <= 3; i++)
  1424.                 {   if
  1425.                     (   worm[i].control != AMIGA
  1426.                      && worm[i].control != NONE
  1427.                      && worm[i].lives
  1428.                     )
  1429.                     {   allowed = FALSE;
  1430.                         break;
  1431.                 }   }
  1432.                 if (allowed)
  1433.                 {   if (turbo)
  1434.                     {   turbo = FALSE;
  1435.                     } else
  1436.                     {   turbo = TRUE;
  1437.                         draw(CLOCKICON, ICONY, CLOCK);
  1438.                 }   }
  1439.             break;
  1440.             case P:
  1441.                 clearkybd();
  1442.                 say("Paused...press P to unpause", WHITE);
  1443.                 pausetimer();
  1444.                 done = FALSE;
  1445.                 while (!done)
  1446.                 {   Wait(1L << MainWindowPtr->UserPort->mp_SigBit);
  1447.                     while (MsgPtr = (struct IntuiMessage *) GetMsg(MainWindowPtr->UserPort))
  1448.                     {   class = MsgPtr->Class;
  1449.                         code  = MsgPtr->Code;
  1450.                         qual  = MsgPtr->Qualifier;
  1451.                         ReplyMsg((struct Message *) MsgPtr);
  1452.                         if (class == IDCMP_RAWKEY && (!(qual & IEQUALIFIER_REPEAT)))
  1453.                         {   if (code == ESCAPE)
  1454.                             {   if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  1455.                                 {   if (verify())
  1456.                                         cleanexit(EXIT_SUCCESS);
  1457.                                 } else
  1458.                                 {   a = GAMEOVER;
  1459.                                     done = TRUE;
  1460.                                     worm[0].lives = worm[1].lives = worm[2].lives = worm[3].lives = 0;
  1461.                             }   }
  1462.                             elif (code == M)
  1463.                                 toggle(M);
  1464.                             elif (code == F)
  1465.                                 toggle(F);
  1466.                             elif (code == P)
  1467.                             {   say("Unpaused", WHITE);
  1468.                                 done = TRUE;
  1469.                 }   }   }   }
  1470.                 unpausetimer();
  1471.             break;
  1472.             case ESCAPE:
  1473.                 if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  1474.                 {   if (verify())
  1475.                         cleanexit(EXIT_SUCCESS);
  1476.                 } else
  1477.                 {   a = GAMEOVER;
  1478.                     worm[0].lives = worm[1].lives = worm[2].lives = worm[3].lives = 0;
  1479.                 }
  1480.             break;
  1481.             default:
  1482.                for (which = 0; which <= NUMKEYS; which++)
  1483.                    if (code == key[which].scancode)
  1484.                        key[which].down = TRUE;
  1485.             break;
  1486.     }   }   }
  1487.     elif (class == IDCMP_CLOSEWINDOW)
  1488.         cleanexit(EXIT_SUCCESS);
  1489.     elif (class == IDCMP_REFRESHWINDOW)
  1490.     {   GT_BeginRefresh(MainWindowPtr);
  1491.         GT_EndRefresh(MainWindowPtr, TRUE);
  1492. }   }
  1493.  
  1494. /* Received but ignored: IDCMP_ACTIVEWINDOW, IDCMP_MOUSEBUTTONS and
  1495. IDCMP_INTUITICKS. */
  1496.  
  1497. for (which = 0; which <= NUMKEYS; which++)
  1498. {   if (key[which].down)
  1499.     {   if (key[which].special == ONEHUMAN)
  1500.         {   if
  1501.             (   (worm[0].control == KEYBOARD && worm[1].control != KEYBOARD)
  1502.             )
  1503.                 wormqueue(0, key[which].deltax, key[which].deltay);
  1504.             elif
  1505.             (   (worm[1].control == KEYBOARD && worm[0].control != KEYBOARD)
  1506.             )
  1507.                 wormqueue(1, key[which].deltax, key[which].deltay);
  1508.         } elif (key[which].special == MOVE || key[which].special == AMMO)
  1509.         {   if (worm[key[which].player].control == KEYBOARD)
  1510.                 keyplayer = key[which].player;
  1511.             elif
  1512.             (   key[which].player == 1
  1513.              && (worm[0].control == KEYBOARD && worm[1].control != KEYBOARD)
  1514.             )
  1515.                 keyplayer = 0;
  1516.             elif
  1517.             (   key[which].player == 0
  1518.              && (worm[1].control == KEYBOARD && worm[0].control != KEYBOARD)
  1519.             )
  1520.                 keyplayer = 1;
  1521.             else keyplayer = -1;
  1522.             if (keyplayer != -1)
  1523.             {   wormqueue(keyplayer, key[which].deltax, key[which].deltay);
  1524.         }   }
  1525.         elif (worm[1].lives) /* assumes key[which].special == TRAINER */
  1526.             train(key[which].scancode);
  1527. }   }
  1528. }
  1529.  
  1530. void hiscores(void)
  1531. {   SBYTE which;
  1532.     TEXT  tempstring[NAMELENGTH + 1];
  1533.  
  1534.     /* render hiscores
  1535.  
  1536.     #################################################### # = shadow
  1537.     #   #   #   #                      #     #         % % = shine
  1538.     #   #   #   #                      #     #         %
  1539.     #   #   #   #                      #     #         %
  1540.     #   #   #   #                      #     #         %
  1541.     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */
  1542.  
  1543.     hiframe = 0;
  1544.     SetDrMd(MainWindowPtr->RPort, JAM1);
  1545.     for (which = 0; which <= HISCORES; which++)
  1546.     {   if (hiscore[which].player == -1)
  1547.             SetAPen(MainWindowPtr->RPort, LIGHTGREY);
  1548.         else SetAPen(MainWindowPtr->RPort, worm[hiscore[which].player].colour);
  1549.         RectFill(MainWindowPtr->RPort, 104, TSOFFSET + 1 + (which * HISCOREDISTANCE), 536, TSOFFSET + 10 + (which * HISCOREDISTANCE));
  1550.     if (hiscore[which].player == 0)
  1551.             SetAPen(MainWindowPtr->RPort, DARKGREEN);
  1552.         elif (hiscore[which].player == 1)
  1553.             SetAPen(MainWindowPtr->RPort, DARKRED);
  1554.         elif (hiscore[which].player == 2)
  1555.             SetAPen(MainWindowPtr->RPort, DARKBLUE);
  1556.         elif (hiscore[which].player == 3)
  1557.             SetAPen(MainWindowPtr->RPort, DARKYELLOW);
  1558.     else SetAPen(MainWindowPtr->RPort, DARKGREY);
  1559.         Move(MainWindowPtr->RPort, 103, TSOFFSET + 11 + (which * HISCOREDISTANCE));
  1560.         Draw(MainWindowPtr->RPort, 103, TSOFFSET +      (which * HISCOREDISTANCE));
  1561.         Draw(MainWindowPtr->RPort, 537, TSOFFSET +      (which * HISCOREDISTANCE));
  1562.  
  1563.     if (hiscore[which].player != -1)
  1564.         {   /* divider bars */
  1565.     
  1566.             Move(MainWindowPtr->RPort, 182 - 55, TSOFFSET +      (which * HISCOREDISTANCE));
  1567.             Draw(MainWindowPtr->RPort, 182 - 55, TSOFFSET + 10 + (which * HISCOREDISTANCE));
  1568.             Move(MainWindowPtr->RPort, 254 - 55, TSOFFSET +      (which * HISCOREDISTANCE));
  1569.             Draw(MainWindowPtr->RPort, 254 - 55, TSOFFSET + 10 + (which * HISCOREDISTANCE));
  1570.             Move(MainWindowPtr->RPort, 290 - 55, TSOFFSET +      (which * HISCOREDISTANCE));
  1571.             Draw(MainWindowPtr->RPort, 290 - 55, TSOFFSET + 10 + (which * HISCOREDISTANCE));
  1572.             Move(MainWindowPtr->RPort, 416,      TSOFFSET +      (which * HISCOREDISTANCE));
  1573.             Draw(MainWindowPtr->RPort, 416,      TSOFFSET + 10 + (which * HISCOREDISTANCE));
  1574.             Move(MainWindowPtr->RPort, 464,      TSOFFSET +      (which * HISCOREDISTANCE));
  1575.             Draw(MainWindowPtr->RPort, 464,      TSOFFSET + 10 + (which * HISCOREDISTANCE));
  1576.     }
  1577.     
  1578.     SetAPen(MainWindowPtr->RPort, WHITE);
  1579.         Move(MainWindowPtr->RPort, 159 - 55, TSOFFSET + 11 + (which * HISCOREDISTANCE));
  1580.         Draw(MainWindowPtr->RPort, 482 + 55, TSOFFSET + 11 + (which * HISCOREDISTANCE));
  1581.         Draw(MainWindowPtr->RPort, 482 + 55, TSOFFSET +  1 + (which * HISCOREDISTANCE));
  1582.     SetAPen(MainWindowPtr->RPort, BLACK);
  1583.  
  1584.     if (hiscore[which].player != -1)
  1585.         {   stci_d(tempstring, which + 1);
  1586.             tempstring[1] = '.';
  1587.             Move(MainWindowPtr->RPort, 161 - 55, TSOFFSET + 8 + (which * HISCOREDISTANCE));
  1588.             Text(MainWindowPtr->RPort, tempstring, 2);
  1589.             stci_d(tempstring, hiscore[which].score);
  1590.             align(tempstring, 7, ' ');
  1591.             Move(MainWindowPtr->RPort, 193 - 55, TSOFFSET + 8 + (which * HISCOREDISTANCE));
  1592.             Text(MainWindowPtr->RPort, tempstring, 7);
  1593.             if (hiscore[which].level == -1)
  1594.             {   strcpy(tempstring, "All");
  1595.             } else
  1596.             {   stci_d(tempstring, hiscore[which].level);
  1597.                 align(tempstring, 3, ' ');
  1598.             }
  1599.             Move(MainWindowPtr->RPort, 206, TSOFFSET + 8 + (which * HISCOREDISTANCE));
  1600.             Text(MainWindowPtr->RPort, tempstring, 3);
  1601.             Move(MainWindowPtr->RPort, 241, TSOFFSET + 8 + (which * HISCOREDISTANCE));
  1602.             Text(MainWindowPtr->RPort, hiscore[which].name, strlen(hiscore[which].name));
  1603.             Move(MainWindowPtr->RPort, 418, TSOFFSET + 8 + (which * HISCOREDISTANCE));
  1604.             Text(MainWindowPtr->RPort, hiscore[which].time, strlen(hiscore[which].time));
  1605.             Move(MainWindowPtr->RPort, 466, TSOFFSET + 8 + (which * HISCOREDISTANCE));
  1606.             Text(MainWindowPtr->RPort, hiscore[which].date, strlen(hiscore[which].date));
  1607.             Image.ImageData = ImageData[missileframes[hiscore[which].player][0]];
  1608.             DrawImage
  1609.             (   MainWindowPtr->RPort,
  1610.                 &Image,
  1611.                 544,
  1612.                 TSOFFSET + 3 + (which * 14)
  1613.             );
  1614.     }   }
  1615.     SetDrMd(MainWindowPtr->RPort, JAM2);
  1616. }
  1617.  
  1618. void hiscorenames(void)
  1619. {   ULONG                class;
  1620.     ABOOL                done;
  1621.     SBYTE                which;
  1622.     struct IntuiMessage* MsgPtr;
  1623.  
  1624.     for (which = 0; which <= HISCORES; which++)
  1625.     {   if (hiscore[which].fresh)
  1626.         {   GT_SetGadgetAttrs(StringGadgetPtr[which], MainWindowPtr, NULL, GA_Disabled, FALSE, GTST_String, worm[hiscore[which].player].name, TAG_DONE);
  1627.             ActivateGadget(StringGadgetPtr[which], MainWindowPtr, NULL);
  1628.             done = FALSE;
  1629.             while (!done)
  1630.             {   while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  1631.                 {   class = MsgPtr->Class;
  1632.                     GT_ReplyIMsg(MsgPtr);
  1633.                     if (class == IDCMP_GADGETUP)
  1634.                     {   done = TRUE;
  1635.                     } elif (class == IDCMP_MOUSEBUTTONS)
  1636.                     {   ActivateGadget(StringGadgetPtr[which], MainWindowPtr, NULL);
  1637.                     } elif (class == IDCMP_REFRESHWINDOW)
  1638.                     {   GT_BeginRefresh(MainWindowPtr);
  1639.                         GT_EndRefresh(MainWindowPtr, TRUE);
  1640.             }   }   }
  1641.             GT_SetGadgetAttrs(StringGadgetPtr[which], MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
  1642.             effect(FXAPPLAUSE);
  1643.             strcpy(hiscore[which].name, ((struct StringInfo *) (StringGadgetPtr[which]->SpecialInfo))->Buffer);
  1644.             if (hiscore[which].name[0] >= 'a' && hiscore[which].name[0] <= 'z')
  1645.             {   hiscore[which].name[0] -= 32;
  1646.             }
  1647.             strcpy(worm[hiscore[which].player].name, hiscore[which].name);
  1648.             hiscore[which].fresh = FALSE;
  1649.             hiscores();
  1650. }   }   }
  1651.  
  1652. MODULE ABOOL beginfx(void)
  1653. {           SBYTE    i;
  1654.     PERSIST    UBYTE    chan[]    = {15};
  1655.  
  1656.     for (i = 0; i <= 3; i++)
  1657.     {   eversent[i] = FALSE;
  1658.         if (!(AudioPortPtr[i] = (struct MsgPort *) CreateMsgPort()))
  1659.         {   freefx();
  1660.             draw(MUSICICON, ICONY, BLACKENED);
  1661.             mode = FALSE;
  1662.             say("No port for effects!", RED);
  1663.             anykey(TRUE);
  1664.             return FALSE;
  1665.         } else if (!(AudioRqPtr[i] = (struct IOAudio *) CreateIORequest(AudioPortPtr[i], sizeof(struct IOAudio))))
  1666.         {   freefx();
  1667.             draw(MUSICICON, ICONY, BLACKENED);
  1668.             mode = FALSE;
  1669.             say("No I/O memory for effects!", RED);
  1670.             anykey(TRUE);
  1671.             return FALSE;
  1672.     }   }
  1673.     AudioRqPtr[0]->ioa_Request.io_Message.mn_ReplyPort      = AudioPortPtr[0];
  1674.     AudioRqPtr[0]->ioa_Request.io_Message.mn_Node.ln_Pri    = 127;
  1675.     AudioRqPtr[0]->ioa_AllocKey                             = 0;
  1676.     AudioRqPtr[0]->ioa_Data                                 = chan;
  1677.     AudioRqPtr[0]->ioa_Length                               = 1;
  1678.     if (AudioClosed = OpenDevice(AUDIONAME, 0L, (struct IORequest *) AudioRqPtr[0], 0L))
  1679.     {   freefx();
  1680.         draw(MUSICICON, ICONY, BLACKENED);
  1681.         mode = FALSE;
  1682.         say("Can't allocate all channels for effects!", RED);
  1683.         anykey(TRUE);
  1684.         return FALSE;
  1685.     } else
  1686.     {   for (i = 1; i <= 3; i++)
  1687.             CopyMem(AudioRqPtr[0], AudioRqPtr[i], sizeof(struct IOAudio));
  1688.         return TRUE;
  1689. }   }
  1690.  
  1691. void resettime(void)
  1692. {   GetSysTime(StartValPtr);
  1693.     srand((UWORD) StartValPtr->tv_micro);
  1694. }
  1695.  
  1696. void rundown(SBYTE player)
  1697. {       ABOOL                done = FALSE, flag = FALSE;
  1698.     TEXT                 tempstring[6];
  1699.     SBYTE                i, j, x, y;
  1700.         UBYTE                multiply = worm[player].multi * players;
  1701.     UWORD                counter = 0, code, qual;
  1702.     ULONG                levelbonus, timebonus, tailbonus, class;
  1703.     struct IntuiMessage* MsgPtr;
  1704.  
  1705.     clearscreen();
  1706.     SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1707.  
  1708.     Image.ImageData = ImageData[TREASURE];
  1709.     DrawImage(MainWindowPtr->RPort, &Image, 201, 103);
  1710.     Move(MainWindowPtr->RPort, 218, 108);
  1711.     Text(MainWindowPtr->RPort, "Level Bonus:    ## x ##00 =", 27);
  1712.         stci_d(tempstring, multiply);
  1713.     align(tempstring, 2, ' ');
  1714.     Move(MainWindowPtr->RPort, 218 + (FONTX * 21), 108);
  1715.     Text(MainWindowPtr->RPort, tempstring, 2);
  1716.     stci_d(tempstring, level - 1);
  1717.     align(tempstring, 2, ' ');
  1718.     Move(MainWindowPtr->RPort, 218 + (FONTX * 16), 108);
  1719.     Text(MainWindowPtr->RPort, tempstring, 2);
  1720.  
  1721.     Image.ImageData = ImageData[CLOCK];
  1722.     DrawImage(MainWindowPtr->RPort, &Image, 201, 111);
  1723.     Move(MainWindowPtr->RPort, 218, 116);
  1724.     Text(MainWindowPtr->RPort, " Time Bonus: ##:## x  ##0 =", 27);
  1725.         stci_d(tempstring, multiply);
  1726.     align(tempstring, 2, ' ');
  1727.     Move(MainWindowPtr->RPort, 226 + (FONTX     * 21), 116);
  1728.     Text(MainWindowPtr->RPort, tempstring, 2);
  1729.     stci_d(tempstring, secondsleft / 60);
  1730.     align(tempstring, 2, ' ');
  1731.     Move(MainWindowPtr->RPort, 226 + (FONTX * 12), 116);
  1732.     Text(MainWindowPtr->RPort, tempstring, 2);
  1733.     stci_d(tempstring, secondsleft % 60);
  1734.     align(tempstring, 2, '0');
  1735.     Move(MainWindowPtr->RPort, 226 + (FONTX * 15), 116);
  1736.     Text(MainWindowPtr->RPort, tempstring, 2);
  1737.  
  1738.     Image.ImageData = ImageData[FIRSTTAIL + player];
  1739.     DrawImage(MainWindowPtr->RPort, &Image, 201, 119);
  1740.     Move(MainWindowPtr->RPort, 218, 124);
  1741.     Text(MainWindowPtr->RPort, " Tail Bonus:  #### x   ## =", 27);
  1742.         stci_d(tempstring, multiply);
  1743.     align(tempstring, 2, ' ');
  1744.         Move(MainWindowPtr->RPort, 226 + (FONTX * 22), 124);
  1745.         Text(MainWindowPtr->RPort, tempstring, 2);
  1746.     for (x = 0; x <= FIELDX; x++)
  1747.         for (y = 0; y <= FIELDY; y++)
  1748.             if (field[x][y] == FIRSTTAIL + player)
  1749.                 counter++;
  1750.     stci_d(tempstring, counter);
  1751.     align(tempstring, 4, ' ');
  1752.     Move(MainWindowPtr->RPort, 226 + (FONTX * 13), 124);
  1753.     Text(MainWindowPtr->RPort, tempstring, 4);
  1754.  
  1755.     levelbonus = (level - 1) * BONUS_LEVEL * multiply;
  1756.     timebonus = secondsleft  * BONUS_TIME  * multiply;
  1757.     tailbonus = counter      * BONUS_TAIL  * multiply;
  1758.  
  1759.     for (i = 0; i <= 3; i++)
  1760.         if (worm[i].control != NONE)
  1761.             for (j = 0; j <= LASTOBJECT; j++)
  1762.                 stat(i, j);
  1763.  
  1764.     while (!done)
  1765.     {   while (MsgPtr = (struct IntuiMessage *) GetMsg(MainWindowPtr->UserPort))
  1766.         {   class  = MsgPtr->Class;
  1767.         code   = MsgPtr->Code;
  1768.         qual   = MsgPtr->Qualifier;
  1769.         ReplyMsg((struct Message *) MsgPtr);
  1770.             if (class == IDCMP_RAWKEY && (!(qual & IEQUALIFIER_REPEAT)) && code < KEYUP)
  1771.                 done = TRUE;
  1772.         }
  1773.         if (done)
  1774.     {   wormscore
  1775.             (   player,
  1776.                 (levelbonus / multiply) +
  1777.                 ( timebonus / multiply) +
  1778.                 ( tailbonus / multiply)
  1779.             );
  1780.             levelbonus = timebonus = tailbonus = 0;
  1781.         }
  1782.         SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1783.         stci_d(tempstring, levelbonus);
  1784.         align(tempstring, 5, ' ');
  1785.         Move(MainWindowPtr->RPort, 218 + (FONTX * 27), 108);
  1786.         Text(MainWindowPtr->RPort, tempstring, 5);
  1787.  
  1788.         stci_d(tempstring, timebonus);
  1789.         align(tempstring, 5, ' ');
  1790.         Move(MainWindowPtr->RPort, 226 + (FONTX * 26), 116);
  1791.         Text(MainWindowPtr->RPort, tempstring, 5);
  1792.  
  1793.         stci_d(tempstring, tailbonus);
  1794.         align(tempstring, 5, ' ');
  1795.         Move(MainWindowPtr->RPort, 226 + (FONTX * 26), 124);
  1796.         Text(MainWindowPtr->RPort, tempstring, 5);
  1797.  
  1798.         done = TRUE;
  1799.         if (levelbonus)
  1800.         {   levelbonus -= multiply;
  1801.             wormscore(player, 1);
  1802.             done = FALSE;
  1803.         }
  1804.         if (timebonus)
  1805.         {   timebonus -= multiply;
  1806.             wormscore(player, 1);
  1807.             done = FALSE;
  1808.         }
  1809.         if (tailbonus)
  1810.         {   tailbonus -= multiply;
  1811.             wormscore(player, 1);
  1812.             done = FALSE;
  1813.         }
  1814.         effect(FXCLICK);
  1815.     }
  1816.     clearkybd();
  1817.     effect(FXRIFF);
  1818.     for (i = 0; i <= 3; i++)
  1819.         if (worm[i].control == KEYBOARD || worm[i].control == JOYSTICK)
  1820.         {   flag = TRUE;
  1821.             break;
  1822.         }
  1823.     waitasec();
  1824.     if (flag)
  1825.         anykey(FALSE);
  1826. }
  1827.  
  1828. void say(STRPTR sentence, COLOUR colour)
  1829. {   SWORD length  = (SBYTE) strlen(sentence),
  1830.           leftx,
  1831.           centrex,
  1832.           rightx;
  1833.  
  1834.     if (iso && a == PLAYGAME)
  1835.     {   centrex = (SCREENXPIXEL / 2) + 44;
  1836.         leftx = STARTXPIXEL + 84;
  1837.         rightx = ENDXPIXEL + 4;
  1838.     } else
  1839.     {   centrex = SCREENXPIXEL / 2;
  1840.         leftx = STARTXPIXEL + 40;
  1841.         rightx = ENDXPIXEL - 40;
  1842.     }
  1843.  
  1844.     /* truncate text */
  1845.     if (length > SAYLIMIT)
  1846.     {   *(sentence + SAYLIMIT) = 0;
  1847.         length = SAYLIMIT;
  1848.     }
  1849.  
  1850.     /* clear areas to left and right of text, respectively */
  1851.     SetAPen(MainWindowPtr->RPort, BLACK);
  1852.     RectFill
  1853.     (   MainWindowPtr->RPort,
  1854.         leftx,
  1855.         2,
  1856.         centrex - (length * FONTX / 2),
  1857.         10
  1858.     );
  1859.     RectFill
  1860.     (   MainWindowPtr->RPort,
  1861.         centrex + (length * FONTX / 2) + 1,
  1862.         2,
  1863.         rightx,
  1864.         10
  1865.     );
  1866.     Move(MainWindowPtr->RPort, centrex - (length * FONTX / 2), 2);
  1867.     Draw(MainWindowPtr->RPort, centrex + (length * FONTX / 2), 2);
  1868.  
  1869.     /* render shadow text */
  1870.     SetAPen(MainWindowPtr->RPort, MEDIUMGREY);
  1871.     Move(MainWindowPtr->RPort, centrex - (length * FONTX / 2) + 1, FONTY + 1);
  1872.     Text(MainWindowPtr->RPort, sentence, length);
  1873.     
  1874.     /* render actual text */
  1875.     SetDrMd(MainWindowPtr->RPort, JAM1);
  1876.     SetAPen(MainWindowPtr->RPort, colour);
  1877.     Move(MainWindowPtr->RPort, centrex - (length * FONTX / 2), FONTY);
  1878.     Text(MainWindowPtr->RPort, sentence, length);
  1879.     SetDrMd(MainWindowPtr->RPort, JAM2);
  1880. }
  1881.     
  1882. void stat(SBYTE player, SBYTE line)
  1883. {   ABOOL print = TRUE;
  1884.     SBYTE i, len, theline;
  1885.     TEXT  output[9];
  1886.  
  1887.     strcpy(output, "        "); /* 8 spaces */
  1888.     switch (line)
  1889.     {
  1890.     case BONUS:
  1891.         do
  1892.         {   worm[player].oldscore += (LIFEMODULO - (worm[player].oldscore % LIFEMODULO));
  1893.             if (worm[player].score >= worm[player].oldscore)
  1894.             {   worm[player].lives++;
  1895.                 stat(player, LIFE);
  1896.         }   }
  1897.         while (worm[player].score > worm[player].oldscore);
  1898.         worm[player].oldscore = worm[player].score;
  1899.         if (worm[player].multi > 1)
  1900.             SetAPen(MainWindowPtr->RPort, WHITE);
  1901.         else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1902.         stcl_d(output, worm[player].score);
  1903.         theline = 0;
  1904.         if (worm[player].score <= 999999)
  1905.         {   len = 6;
  1906.             if (player == 1 && iso && worm[1].score <= 99999)
  1907.                 align(output, 6, ' ');
  1908.             else
  1909.                 for (i = 0; i <= 8; i++)
  1910.                     if (!output[i])
  1911.                         output[i] = ' ';
  1912.         } else
  1913.         {   len = 8;
  1914.             for (i = 0; i <= 8; i++)
  1915.                 if (!output[i])
  1916.                     output[i] = ' ';
  1917.             // assert(worm[player].score <= 99999999);
  1918.         }
  1919.     break;
  1920.     case LIFE:
  1921.         if (worm[player].lives > STARTLIVES)
  1922.         {   SetAPen(MainWindowPtr->RPort, WHITE);
  1923.             if (worm[player].lives > LIVESLIMIT)
  1924.                 worm[player].lives = LIVESLIMIT;
  1925.         } else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1926.         stci_d(output, worm[player].lives);
  1927.         if (player == 1 && iso)
  1928.         {   len = 6;
  1929.             align(output, 6, ' ');
  1930.         } else
  1931.         {   len = 3;
  1932.             for (i = 0; i <= 2; i++)
  1933.                 if (!output[i])
  1934.                     output[i] = ' ';
  1935.         }
  1936.         theline = 1;
  1937.     break;
  1938.     case NITRO:
  1939.             if (worm[player].nitro)
  1940.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1941.             else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1942.             if (worm[player].speed == FAST)
  1943.                 strcpy(output, "Fast  ");
  1944.             elif (worm[player].speed == NORMAL)
  1945.                 strcpy(output, "Normal");
  1946.             elif (worm[player].speed == SLOW)
  1947.                 strcpy(output, "Slow  ");
  1948.             else
  1949.             {   // assert(worm[player].speed == VERYSLOW);
  1950.                 strcpy(output, "V.Slow");
  1951.             }
  1952.             theline = 2;
  1953.             len = 6;
  1954.         break;
  1955.     case AMMO:
  1956.             if (worm[player].ammo)
  1957.             {   if (worm[player].ammo > AMMOLIMIT)
  1958.                     worm[player].ammo = AMMOLIMIT;
  1959.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1960.             } else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1961.             stci_d(output, worm[player].ammo);
  1962.             for (i = 0; i <= 2; i++)
  1963.                 if (!output[i])
  1964.                     output[i] = ' ';
  1965.             if (iso)
  1966.             {   theline = 3;
  1967.             } else theline = 4;
  1968.             len = 3;
  1969.         break;
  1970.     case POWER:
  1971.             switch(worm[player].power)
  1972.             {
  1973.             case 0:
  1974.                 SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1975.                 strcpy(output, "Single");
  1976.             break;
  1977.             case 2:
  1978.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1979.                 strcpy(output, "Triple");
  1980.             break;
  1981.             case 4:
  1982.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1983.                 strcpy(output, "Quint.");
  1984.             break;
  1985.             case 6:
  1986.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1987.                 strcpy(output, "Sept. ");
  1988.             break;
  1989.             default:
  1990.             break;
  1991.             }
  1992.             if (iso)
  1993.                 theline = 4;
  1994.             else theline = 5;
  1995.             len = 6;
  1996.         break;
  1997.     case ARMOUR:
  1998.             if (worm[player].armour > MODELIMIT)
  1999.                 worm[player].armour = MODELIMIT;
  2000.             if (worm[player].armour > 0)
  2001.                 SetAPen(MainWindowPtr->RPort, WHITE);
  2002.             else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  2003.             stci_d(output, worm[player].armour);
  2004.             for (i = 0; i <= 2; i++)
  2005.                 if (!output[i])
  2006.                     output[i] = ' ';
  2007.             if (iso)
  2008.                 theline = 5;
  2009.             else theline = 7;
  2010.             len = 3;
  2011.         break;
  2012.     case BIAS:
  2013.         if (worm[player].bias > 0)
  2014.         {   if (worm[player].bias > BIASLIMIT)
  2015.                 worm[player].bias = BIASLIMIT;
  2016.             SetAPen(MainWindowPtr->RPort, WHITE);
  2017.         } else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  2018.         stci_d(output, worm[player].bias);
  2019.         for (i = 0; i <= 2; i++)
  2020.             if (!output[i])
  2021.                 output[i] = ' ';
  2022.         if (iso)
  2023.             theline = 6;
  2024.         else theline = 8;
  2025.         len = 3;
  2026.     break;
  2027.     default:
  2028.         print = FALSE;
  2029.         /* These next two lines are just to prevent spurious compiler
  2030.         warnings about possibly uninitialized variables */
  2031.         theline = 0;
  2032.         len = 0;
  2033.     break;
  2034.     }
  2035.  
  2036.     /* Sometimes stat() is called with a valid line, yet an invalid player.
  2037.     Defensive programming currently ensures that this is not a problem, but
  2038.     it would be better to fix the bug. */
  2039.  
  2040.     if (print)
  2041.     {   if (iso)
  2042.         {   switch(player)
  2043.             {
  2044.             case 0:
  2045.                 Move(MainWindowPtr->RPort, 0  ,  17 + (theline * FONTY));
  2046.             break;
  2047.             case 1:
  2048.                 Move(MainWindowPtr->RPort, 528, 196 + (theline * FONTY));
  2049.             break;
  2050.             case 2:
  2051.                 Move(MainWindowPtr->RPort, 66 ,  17 + (theline * FONTY));
  2052.             break;
  2053.             case 3:
  2054.                 Move(MainWindowPtr->RPort, 592, 196 + (theline * FONTY));
  2055.             break;
  2056.             default:
  2057.                 // assert(0);
  2058.             break;
  2059.         }   }
  2060.         else
  2061.         {   Move
  2062.             (   MainWindowPtr->RPort,
  2063.                 (FONTX * 3) + (worm[player].statx * ENDXPIXEL),
  2064.                 STARTYPIXEL + 6 + (theline * FONTY) + (worm[player].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
  2065.             );
  2066.         }
  2067.         Text(MainWindowPtr->RPort, output, len);
  2068. }   }
  2069.  
  2070. void stopfx(void)
  2071. {   SBYTE i;
  2072.  
  2073.     if (mode == FX)
  2074.         for (i = 0; i <= 3; i++)
  2075.             // CheckIO() returns NULL if complete, pointer if incomplete? (!)
  2076.             if (eversent[i] && (!(CheckIO((struct IORequest *) AudioRqPtr[i]))))
  2077.             {   AbortIO((struct IORequest *) AudioRqPtr[i]);
  2078.                 WaitIO((struct IORequest *) AudioRqPtr[i]);
  2079. }           }
  2080.  
  2081. void titlescreen(void)
  2082. {   SBYTE                object = LASTOBJECT, player, i;
  2083.     SWORD                descx = FIRSTDESCX;
  2084.     ULONG                class;
  2085.     UWORD                code, qual;
  2086.     struct IntuiMessage* MsgPtr;
  2087.     struct Gadget*       WhichGadgetPtr;
  2088.     struct MenuItem*     ItemPtr;
  2089.  
  2090.     effect(FXLAUNCH);
  2091.     Forbid();
  2092.     MainWindowPtr->Flags &= ~WFLG_RMBTRAP;
  2093.     Permit();
  2094.     clearscreen();
  2095.  
  2096.     SetMenuStrip(MainWindowPtr, MenuPtr);
  2097.     for (player = 0; player <= 3; player++)
  2098.     {   GT_SetGadgetAttrs(CycleGadgetPtr[player], MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
  2099.     }
  2100.     GT_SetGadgetAttrs(RandomGadgetPtr, MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
  2101.     GT_SetGadgetAttrs(CheckboxGadgetPtr, MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
  2102.  
  2103.     clearkybd();
  2104.     hiscores();
  2105.     hiscorenames();
  2106.  
  2107.     SetDrMd(MainWindowPtr->RPort, JAM1);
  2108.     SetAPen(MainWindowPtr->RPort, BLACK);
  2109.     Move(MainWindowPtr->RPort, STARTXPIXEL + 9, TSOFFSET +  86);
  2110.     Text(MainWindowPtr->RPort, "Spacebar: Field Editor ", 23);
  2111.     Move(MainWindowPtr->RPort, STARTXPIXEL + 9, TSOFFSET +  98);
  2112.     Text(MainWindowPtr->RPort, "       F: Sound Effects", 23);
  2113.     Move(MainWindowPtr->RPort, STARTXPIXEL + 9, TSOFFSET + 110);
  2114.     Text(MainWindowPtr->RPort, "       M: Music        ", 23);
  2115.     SetAPen(MainWindowPtr->RPort, WHITE);
  2116.     Move(MainWindowPtr->RPort, STARTXPIXEL + 8, TSOFFSET +  85);
  2117.     Text(MainWindowPtr->RPort, "Spacebar: Field Editor ", 23);
  2118.     Move(MainWindowPtr->RPort, STARTXPIXEL + 8, TSOFFSET +  97);
  2119.     Text(MainWindowPtr->RPort, "       F: Sound Effects", 23);
  2120.     Move(MainWindowPtr->RPort, STARTXPIXEL + 8, TSOFFSET + 109);
  2121.     Text(MainWindowPtr->RPort, "       M: Music        ", 23);
  2122.     SetDrMd(MainWindowPtr->RPort, JAM2);
  2123.  
  2124.     DrawImage(MainWindowPtr->RPort, &Logo, 203, 22);
  2125.  
  2126.     do
  2127.     {   TimerRqPtr->tr_node.io_Command  = TR_ADDREQUEST;
  2128.         TimerRqPtr->tr_time.tv_secs     = 0;
  2129.         TimerRqPtr->tr_time.tv_micro    = ANIMDELAY;
  2130.         SendIO((struct IORequest *) TimerRqPtr);
  2131.  
  2132.         if (descx == FIRSTDESCX)
  2133.         {   if (++object > LASTOBJECT)
  2134.             {   object = 0;
  2135.             }
  2136.             say(objectdesc[object], WHITE);
  2137.             Image.ImageData = ImageData[object];
  2138.             DrawImage(MainWindowPtr->RPort, &Image, SECONDDESCX, DESCY);
  2139.         }
  2140.         SetAPen(MainWindowPtr->RPort, BLACK);
  2141.         Move(MainWindowPtr->RPort, descx - 1, DESCY - 1);
  2142.         Draw(MainWindowPtr->RPort, descx - 1, DESCY + 1 + SQUAREY);
  2143.         Move(MainWindowPtr->RPort, descx + 1 + SQUAREX, DESCY - 1);
  2144.         Draw(MainWindowPtr->RPort, descx + 1 + SQUAREX, DESCY + 1 + SQUAREY);
  2145.         Image.ImageData = ImageData[object];
  2146.         DrawImage(MainWindowPtr->RPort, &Image, descx, DESCY);
  2147.         if (++descx > SECONDDESCX)
  2148.         {   descx = FIRSTDESCX;
  2149.         }
  2150.  
  2151.         for (i = 0; i <= HISCORES; i++)
  2152.         {   if (anims && hiscore[i].player != -1)
  2153.             {   Image.ImageData = ImageData[missileframes[hiscore[i].player][hiframe]];
  2154.                 DrawImage
  2155.                 (   MainWindowPtr->RPort,
  2156.                     &Image,
  2157.                     544,
  2158.                     TSOFFSET + 3 + (i * 14)
  2159.                 );
  2160.                 if (++hiframe > MISSILEFRAMES)
  2161.                 {   hiframe = 0;
  2162.         }   }   }
  2163.  
  2164.         while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  2165.         {   WhichGadgetPtr = (struct Gadget *) MsgPtr->IAddress;
  2166.             class = MsgPtr->Class;
  2167.             code  = MsgPtr->Code;
  2168.             qual  = MsgPtr->Qualifier;
  2169.             GT_ReplyIMsg(MsgPtr);
  2170.             switch (class)
  2171.             {
  2172.             case IDCMP_RAWKEY:
  2173.                 if (!(qual & IEQUALIFIER_REPEAT))
  2174.                 {   switch (code)
  2175.                     {
  2176.                     case F:
  2177.                         toggle(F);
  2178.                     break;
  2179.                     case M:
  2180.                         toggle(M);
  2181.                     break;
  2182.                     case SPACEBAR:
  2183.                         effect(FXCLICK);
  2184.                         a = FIELDEDIT;
  2185.                     break;
  2186.                     case F1:
  2187.                     case ALPHAONE:
  2188.                     case NUMERICONE:
  2189.                         effect(FXCLICK);
  2190.                         if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  2191.                         {   if (--worm[0].control < 0)
  2192.                                 worm[0].control = 4;
  2193.                         } else
  2194.                         {   if (++worm[0].control > 4)
  2195.                                 worm[0].control = 0;
  2196.                         }
  2197.                         GT_SetGadgetAttrs(CycleGadgetPtr[0], MainWindowPtr, NULL, GTCY_Active, worm[0].control, TAG_DONE);
  2198.                     break;
  2199.                     case F2:
  2200.                     case ALPHATWO:
  2201.                     case NUMERICTWO:
  2202.                         effect(FXCLICK);
  2203.                         if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  2204.                         {   if (--worm[1].control < 0)
  2205.                                 worm[1].control = 4;
  2206.                         } else
  2207.                         {   if (++worm[1].control > 4)
  2208.                                 worm[1].control = 0;
  2209.                         }
  2210.                         GT_SetGadgetAttrs(CycleGadgetPtr[1], MainWindowPtr, NULL, GTCY_Active, worm[1].control, TAG_DONE);
  2211.                     break;
  2212.                     case F3:
  2213.                     case ALPHATHREE:
  2214.                     case NUMERICTHREE:
  2215.                         effect(FXCLICK);
  2216.                         if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  2217.                         {   if (worm[2].control > 0)
  2218.                             {   worm[2].control--;
  2219.                             } else worm[2].control = 3;
  2220.                         } else
  2221.                         {   if (worm[2].control < 3)
  2222.                             {   worm[2].control++;
  2223.                             } else worm[2].control = 0;
  2224.                         }
  2225.                         GT_SetGadgetAttrs(CycleGadgetPtr[2], MainWindowPtr, NULL, GTCY_Active, worm[2].control, TAG_DONE);
  2226.                     break;
  2227.                     case F4:
  2228.                     case ALPHAFOUR:
  2229.                     case NUMERICFOUR:
  2230.                         effect(FXCLICK);
  2231.                         if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  2232.                         {   if (--worm[3].control < 0)
  2233.                                 worm[3].control = 3;
  2234.                         } else
  2235.                         {   if (++worm[3].control > 3)
  2236.                                 worm[3].control = 0;
  2237.                         }
  2238.                         GT_SetGadgetAttrs(CycleGadgetPtr[3], MainWindowPtr, NULL, GTCY_Active, worm[3].control, TAG_DONE);
  2239.                     break;
  2240.                     case RETURN:
  2241.                     case ENTER:
  2242.                         a = PLAYGAME;
  2243.                     break;
  2244.                     case I:
  2245.                         effect(FXCLICK);
  2246.                         iso = !iso;
  2247.                         GT_SetGadgetAttrs(CheckboxGadgetPtr, MainWindowPtr, NULL, GTCB_Checked, iso, TAG_DONE);
  2248.                     break;
  2249.                     case S:
  2250.                         effect(FXCLICK);
  2251.                         randomflag = !randomflag;
  2252.                         GT_SetGadgetAttrs(RandomGadgetPtr, MainWindowPtr, NULL, GTCB_Checked, randomflag, TAG_DONE);
  2253.                     break;
  2254.                     case ESCAPE:
  2255.                         if (verify())
  2256.                             cleanexit(EXIT_SUCCESS);
  2257.                     break;
  2258.                     case HELP:
  2259.                         helpabout();
  2260.                     break;
  2261.                     default:
  2262.                     break;
  2263.                 }   }
  2264.             break;
  2265.             case IDCMP_MENUPICK:
  2266.                 while (code != MENUNULL)
  2267.                 {   ItemPtr = ItemAddress(MenuPtr, code);
  2268.  
  2269.                     switch (MENUNUM(code))
  2270.                     {
  2271.                     case MN_PROJECT:
  2272.                         switch (ITEMNUM(code))
  2273.                         {
  2274.                         case IN_NEW:
  2275.                             effect(FXFILENEW);
  2276.                             newfields();
  2277.                             say("New done.", WHITE);
  2278.                             anykey(TRUE);
  2279.                             say(objectdesc[object], WHITE);
  2280.                         break;
  2281.                         case IN_OPEN:
  2282.                             effect(FXFILEOPEN);
  2283.                             fileopen(FALSE);
  2284.                             say(objectdesc[object], WHITE);
  2285.                         break;
  2286.                         case IN_REVERT:
  2287.                             fileopen(TRUE);
  2288.                             say(objectdesc[object], WHITE);
  2289.                         break;
  2290.                         case IN_SAVE:
  2291.                             effect(FXFILESAVE);
  2292.                             filesaveas(FALSE);
  2293.                             say(objectdesc[object], WHITE);
  2294.                         break;
  2295.                         case IN_SAVEAS:
  2296.                             effect(FXFILESAVEAS);
  2297.                             filesaveas(TRUE);
  2298.                             say(objectdesc[object], WHITE);
  2299.                         break;
  2300.                         case IN_PROJECTDELETE:
  2301.                             effect(FXEDITDELETE);
  2302.                             filedelete();
  2303.                         break;
  2304.                         case IN_QUIT:
  2305.                             if (verify())
  2306.                                 cleanexit(EXIT_SUCCESS);
  2307.                         break;
  2308.                         default:
  2309.                         break;
  2310.                         }
  2311.                     break;
  2312.                     case MN_SETTINGS:
  2313.                         switch(ITEMNUM(code))
  2314.                         {
  2315.                         case IN_ANIMATIONS:
  2316.                             if (ItemPtr->Flags & CHECKED)
  2317.                             {   anims = TRUE;
  2318.                             } else
  2319.                             {   anims = FALSE;
  2320.                             }
  2321.                         break;
  2322.                         case IN_CREATEICONS:
  2323.                             if (ItemPtr->Flags & CHECKED)
  2324.                             {   icons = TRUE;
  2325.                             } else
  2326.                             {   icons = FALSE;
  2327.                             }
  2328.                         break;
  2329.                         default:
  2330.                         break;
  2331.                         }
  2332.                     break;
  2333.                     case MN_HELP:
  2334.                         switch(ITEMNUM(code))
  2335.                         {
  2336.                         case IN_MANUAL:
  2337.                             helpmanual();
  2338.                         break;
  2339.                         case IN_ABOUT:
  2340.                             helpabout();
  2341.                         break;
  2342.                         case IN_CREATURES:
  2343.                             help(ORB);
  2344.                         break;
  2345.                         case IN_OBJECTS:
  2346.                             help(AFFIXER);
  2347.                         break;
  2348.                         default:
  2349.                         break;
  2350.                         }
  2351.                     break;
  2352.                     default:
  2353.                     break;
  2354.                     }
  2355.                     code = ItemPtr->NextSelect;
  2356.                 }
  2357.             break;
  2358.             case IDCMP_MOUSEBUTTONS:
  2359.                 if (code == SELECTDOWN)
  2360.                 {   if (ignore)
  2361.                     {   ignore = FALSE;
  2362.                     } else
  2363.                     {   a = PLAYGAME;
  2364.                 }   }
  2365.             break;
  2366.             case IDCMP_REFRESHWINDOW:
  2367.                 GT_BeginRefresh(MainWindowPtr);
  2368.                 GT_EndRefresh(MainWindowPtr, TRUE);
  2369.             break;
  2370.             case IDCMP_GADGETUP:
  2371.                 if (WhichGadgetPtr == CheckboxGadgetPtr)
  2372.                 {   iso = !iso;
  2373.                 } elif (WhichGadgetPtr == RandomGadgetPtr)
  2374.                 {   randomflag = !randomflag;
  2375.                 } else
  2376.                 {   for (player = 0; player <= 3; player++)
  2377.                     {   if (WhichGadgetPtr == CycleGadgetPtr[player])
  2378.                         {   worm[player].control = code;
  2379.                             break;
  2380.                 }   }   }
  2381.             break;
  2382.             case IDCMP_ACTIVEWINDOW:
  2383.                 ignore = TRUE;
  2384.             break;
  2385.             case IDCMP_CLOSEWINDOW:
  2386.                 cleanexit(EXIT_SUCCESS);
  2387.             break;
  2388.             default:
  2389.                 ; /* IDCMP_MENUVERIFY, IDCMP_INTUITICKS */
  2390.             break;
  2391.         }   }
  2392.  
  2393.         if (firebutton())
  2394.         {   a = PLAYGAME;
  2395.         }
  2396.         if (a == PLAYGAME)
  2397.         {   if (worm[0].control == NONE && worm[1].control == NONE && worm[2].control == NONE && worm[3].control == NONE)
  2398.             {   say("No worms active!", WHITE);
  2399.                 anykey(TRUE);
  2400.                 a = GAMEOVER;
  2401.             } elif
  2402.             (   !LowLevelBase &&
  2403.                 (worm[0].control == GAMEPAD
  2404.               || worm[1].control == GAMEPAD
  2405.               || worm[2].control == GAMEPAD
  2406.               || worm[3].control == GAMEPAD
  2407.             )   )
  2408.             {   say("Need OS3.1+ for gamepad!", WHITE);
  2409.                 anykey(TRUE);
  2410.                 a = GAMEOVER;
  2411.         }   }
  2412.         // CheckIO() returns NULL if complete, pointer if incomplete.
  2413.         if (CheckIO((struct IORequest *) TimerRqPtr))
  2414.             draw(CLOCKICON, ICONY, CLOCK);
  2415.         else draw(CLOCKICON, ICONY, BLACKENED);
  2416.         WaitIO((struct IORequest *) TimerRqPtr);
  2417.     } while (a == GAMEOVER);
  2418.  
  2419.     for (player = 0; player <= 3; player++)
  2420.     {   GT_SetGadgetAttrs(CycleGadgetPtr[player], MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
  2421.     }
  2422.     GT_SetGadgetAttrs(CheckboxGadgetPtr, MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
  2423.     GT_SetGadgetAttrs(RandomGadgetPtr, MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
  2424.     Image.ImageData = ImageData[BLACKENED];
  2425.     DrawImage(MainWindowPtr->RPort, &Image, FIRSTDESCX, DESCY);
  2426.     DrawImage(MainWindowPtr->RPort, &Image, SECONDDESCX, DESCY);
  2427.  
  2428.     if (a == FIELDEDIT)
  2429.     {   fieldedit();
  2430.     } else
  2431.     {   turbo = FALSE;
  2432.         newgame();
  2433.         clearkybd();
  2434.         Forbid();
  2435.         MainWindowPtr->Flags |= WFLG_RMBTRAP;
  2436.         Permit();
  2437. }   }
  2438.  
  2439. AGLOBAL void toggle(SBYTE key)
  2440. {   PERSIST ABOOL songstarted = FALSE;
  2441.  
  2442.     switch(key)
  2443.     {
  2444.     case F:
  2445.         if (mode == FX) /* F in FX mode: no sound */
  2446.         {   freefx();
  2447.             mode = FALSE;
  2448.             draw(MUSICICON, ICONY, BLACKENED);
  2449.         } else if (fxable != FAILED) /* F otherwise: change to FX mode */
  2450.         {   if (mode == MUSIC) /* stop any music that is playing */
  2451.             {   StopPlayer();
  2452.                 FreePlayer();
  2453.             }
  2454.             if (fxable == DEFER) /* load samples if needed */
  2455.                 loadthefx();
  2456.             if (fxable == SUCCEEDED) /* if we have samples in memory */
  2457.             {   if (beginfx())
  2458.                 {   mode = FX;
  2459.                     effect(FXLAUNCH);
  2460.                     draw(MUSICICON, ICONY, FX);
  2461.         }   }   }
  2462.     break;
  2463.     case M:
  2464.         if (mode == MUSIC) /* M in MUSIC mode: no sound */
  2465.         {   StopPlayer();
  2466.             FreePlayer();
  2467.             mode = FALSE;
  2468.             draw(MUSICICON, ICONY, BLACKENED);
  2469.         } else if (musicable != FAILED) /* M otherwise: change to music mode */
  2470.         {   if (mode == FX) /* stop any samples that are playing */
  2471.                 freefx();
  2472.             /* Of course, these statements are ordered in this
  2473.             way for a reason, so don't change it. :-) */
  2474.             if (musicable == DEFER)
  2475.                 loadthemusic();
  2476.             if (musicable == SUCCEEDED)
  2477.             {   if (GetPlayer(0))
  2478.                 {   say("No channels for music!", RED);
  2479.                     anykey(TRUE);
  2480.                     mode = FALSE;
  2481.                     draw(MUSICICON, ICONY, BLACKENED);
  2482.                 } else
  2483.                 {   if (songstarted)
  2484.                         ContModule(SongPtr);
  2485.                     else
  2486.                     {   PlayModule(SongPtr);
  2487.                         songstarted = TRUE;
  2488.                     }
  2489.                     mode = MUSIC;
  2490.                     draw(MUSICICON, ICONY, MUSIC);
  2491.         }   }   }
  2492.     break;
  2493.     default:
  2494.         flash(2);
  2495.         // assert(0);
  2496.     break;
  2497. }   }
  2498.  
  2499. ABOOL verify(void)
  2500. {    pausetimer();
  2501.     if (modified && (EasyRequest(MainWindowPtr, &EasyStruct, NULL) == 0))
  2502.         return FALSE;
  2503.     else return TRUE;
  2504.     unpausetimer();
  2505. }
  2506.  
  2507. void waitasec(void)
  2508. {   Delay(50);
  2509. }
  2510.  
  2511. void systemsetup(void)
  2512. {    worm[0].control    = NONE;
  2513.     worm[1].control    = KEYBOARD;
  2514.     worm[2].control    = NONE;
  2515.     worm[3].control = AMIGA;
  2516. }
  2517.  
  2518. ABOOL ZOpen(STRPTR fieldname, ABOOL write)
  2519. {    if (!write)
  2520.         if (FilePtr = Open(fieldname, MODE_OLDFILE))
  2521.             return TRUE;
  2522.         else return FALSE;
  2523.     else
  2524.         if (FilePtr = Open(fieldname, MODE_NEWFILE))
  2525.             return TRUE;
  2526.         else return FALSE;
  2527. }
  2528. ABOOL ZRead(STRPTR IOBuffer, ULONG length)
  2529. {    if (Read(FilePtr, IOBuffer, length) == length)
  2530.         return TRUE;
  2531.     else return FALSE;
  2532. }
  2533. ABOOL ZWrite(STRPTR IOBuffer, ULONG length)
  2534. {    if (Write(FilePtr, IOBuffer, length) == length)
  2535.         return TRUE;
  2536.     else return FALSE;
  2537. }
  2538. ABOOL ZClose(void)
  2539. {    if (Close(FilePtr))
  2540.     {    FilePtr = NULL;
  2541.         return TRUE;
  2542.     } else
  2543.     {    /* "If Close() returns DOSFALSE, the user has already cancelled an
  2544.         error requester and the function has already freed the FileHandle
  2545.         (and even marked it so any attempt to close it again will bring up
  2546.         the "Software Failure" requester). Therefore FilePtr should be set
  2547.         to zero in any case." - Jilles Tjoelker. */
  2548.  
  2549.         FilePtr = NULL;
  2550.         return FALSE;
  2551. }    }
  2552.  
  2553. void timing(void)
  2554. {    ;
  2555. }
  2556.  
  2557. MODULE void loadthefx(void)
  2558. {   UBYTE*        p8data;
  2559.     TEXT          saystring[SAYLIMIT + 1];
  2560.     SBYTE         code = 0, i,
  2561.                   iobuffer[8]; /* buffer for 8SVX.VHDR  */
  2562.     SBYTE*        psample[2];  /* sample pointers */
  2563.     struct Chunk* p8Chunk;     /* pointers for 8SVX parsing */
  2564.     Voice8Header* pVoice8Header = NULL; // only set to NULL to avoid spurious warnings
  2565.     ULONG         rd8count;
  2566.  
  2567.     say("Loading sound effects...", WHITE);
  2568.     fxable = SUCCEEDED;
  2569.  
  2570.     for (i = 0; i <= SAMPLES; i++)
  2571.         samp[i].base = NULL;
  2572.  
  2573.     for (i = 0; i <= SAMPLES; i++)
  2574.     {   if (!(FilePtr = Open(samp[i].filename, MODE_OLDFILE)))
  2575.             code = 1;                               /* can't open file */
  2576.         else
  2577.         {   rd8count = Read(FilePtr, iobuffer, 8L);
  2578.             if (rd8count == -1)
  2579.                 code = 2;                           /* can't read file */
  2580.             elif (rd8count < 8)
  2581.                 code = 3;                           /* not an IFF 8SVX; too short */
  2582.             else
  2583.             {   p8Chunk = (struct Chunk *) iobuffer;
  2584.                 if (p8Chunk->ckID != ID_FORM)
  2585.                     code = 4;                       /* not an IFF FORM */
  2586.                 elif (!(fbase = (UBYTE *) AllocMem(fsize = p8Chunk->ckSize, MEMF_PUBLIC | MEMF_CLEAR)))
  2587.                     code = 5;                       /* no memory for read */
  2588.                 else
  2589.                 {   p8data = fbase;
  2590.                     rd8count = Read(FilePtr, p8data, p8Chunk->ckSize);
  2591.                     if (rd8count == -1)
  2592.                         code = 6;                   /* read error */
  2593.                     elif (rd8count < p8Chunk->ckSize)
  2594.                         code = 7;                   /* malformed IFF; too short */
  2595.                     elif (MAKE_ID(*p8data, *(p8data + 1), *(p8data + 2), *(p8data + 3)) != ID_8SVX)
  2596.                         code = 8;                   /* not an IFF 8SVX */
  2597.                     else
  2598.                     {   p8data = p8data + 4;
  2599.                         while (p8data < fbase + fsize)
  2600.                         {   p8Chunk = (struct Chunk *) p8data;
  2601.                             switch(p8Chunk->ckID)
  2602.                             {
  2603.                             case ID_VHDR:
  2604.                                 pVoice8Header     = (Voice8Header *) (p8data + 8L);
  2605.                             break;
  2606.                             case ID_BODY:
  2607.                                 psample[0]        = (SBYTE *) (p8data + 8L);
  2608.                                 psample[1]        = psample[0] + pVoice8Header->oneShotHiSamples;
  2609.                                 samp[i].length[0] = (ULONG) pVoice8Header->oneShotHiSamples;
  2610.                                 samp[i].length[1] = (ULONG) pVoice8Header->repeatHiSamples;
  2611.  
  2612.                                 /* To grab the volume level from the IFF
  2613.                                 8SVX file itself, add this line here:
  2614.  
  2615.                                 samp[i].volume    = (SBYTE) (pVoice8Header->volume / 156); */
  2616.                             break;
  2617.                             default:
  2618.                             break;
  2619.                             }
  2620.                             p8data += 8L + p8Chunk->ckSize;
  2621.                             if (p8Chunk->ckSize & 1L == 1)
  2622.                                 p8data++;
  2623.                         }
  2624.                         if (samp[i].length[0] == 0)
  2625.                             samp[i].bank = 1;
  2626.                         else samp[i].bank = 0;
  2627.                         if (samp[i].length[samp[i].bank] <= 102400)
  2628.                             samp[i].size = samp[i].length[samp[i].bank];
  2629.                         else samp[i].size = 102400;
  2630.                         samp[i].base = (UBYTE *) AllocMem(samp[i].size, MEMF_CHIP | MEMF_CLEAR);
  2631.                         if (!samp[i].base)
  2632.                             code = 9; /* no chip memory */
  2633.                         else
  2634.                         {   CopyMem(psample[samp[i].bank], samp[i].base, samp[i].size);
  2635.                             psample[samp[i].bank] += samp[i].size;
  2636.                             samp[i].speed = PALCLOCK / pVoice8Header->samplesPerSec;
  2637.                             /* perhaps we should have a different
  2638.                             value for DblPAL screens? */
  2639.                             if (fbase)
  2640.                             {   FreeMem(fbase, fsize);
  2641.                                 fbase = NULL;
  2642.                             }
  2643.                             if (FilePtr)
  2644.                             {   Close(FilePtr);
  2645.                                 FilePtr = NULL;
  2646.         }   }   }   }   }   }
  2647.         if (code)
  2648.         {   freefx();
  2649.             fxable = FAILED;
  2650.             strcpy(saystring, samp[i].filename);
  2651.             strcat(saystring, ": ");
  2652.             strcat(saystring, sfxerror[code]);
  2653.             say(saystring, RED);
  2654.             anykey(TRUE);
  2655.             break;
  2656. }   }   }
  2657.  
  2658. MODULE void loadthemusic(void)
  2659. {    if (!(MEDPlayerBase = (struct MEDPlayerBase *) OpenLibrary("medplayer.library", 0L)))
  2660.     {    say("Can't open MEDPlayer.library!", RED);
  2661.         anykey(TRUE);
  2662.     } else
  2663.     {    say("Loading music...", WHITE);
  2664.         if (SongPtr = (struct MMD0 *) LoadModule("PROGDIR:WormWars.MED"))
  2665.                         musicable = SUCCEEDED;
  2666.         else
  2667.         {    say("Can't load music!", RED);
  2668.             anykey(TRUE);
  2669. }    }    }
  2670.  
  2671. void clearscreen(void)
  2672. {   SBYTE i;
  2673.  
  2674.     Background.PlaneOnOff = 8 + ((rand() % 4) * 2);
  2675.     // random background colour
  2676.     DrawImage
  2677.     (   MainWindowPtr->RPort,
  2678.         &Background,
  2679.         0,
  2680.         0
  2681.     );
  2682.     if (mode == MUSIC)
  2683.         draw(MUSICICON, ICONY, MUSIC);
  2684.     else if (mode == FX)
  2685.         draw(MUSICICON, ICONY, FX);
  2686.     else draw(MUSICICON, ICONY, BLACKENED);
  2687.     draw(CLOCKICON, ICONY, CLOCK);
  2688.  
  2689.     if (a != FIELDEDIT)
  2690.     {   if (!iso)
  2691.     {   for (i = 0; i <= 3; i++)
  2692.         {   if (worm[i].control != NONE)
  2693.                 {   Image.ImageData = ImageData[BONUS];
  2694.                     DrawImage
  2695.                     (   MainWindowPtr->RPort,
  2696.                         &Image,
  2697.                         (worm[i].statx * ENDXPIXEL) + FONTX + 4,
  2698.             STARTYPIXEL + 1               + (worm[i].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
  2699.                     );
  2700.                     Image.ImageData = ImageData[LIFE];
  2701.                     DrawImage
  2702.                     (   MainWindowPtr->RPort,
  2703.                         &Image,
  2704.                         (worm[i].statx * ENDXPIXEL) + FONTX + 4,
  2705.                         STARTYPIXEL + 1 + (1 * FONTY) + (worm[i].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
  2706.                     );
  2707.                     Image.ImageData = ImageData[NITRO];
  2708.                     DrawImage
  2709.                     (   MainWindowPtr->RPort,
  2710.                         &Image,
  2711.                         (worm[i].statx * ENDXPIXEL) + FONTX + 4,
  2712.                         STARTYPIXEL + 1 + (2 * FONTY) + (worm[i].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
  2713.                     );
  2714.  
  2715.                     Image.ImageData = ImageData[AMMO];
  2716.                     DrawImage
  2717.                     (   MainWindowPtr->RPort,
  2718.                         &Image,
  2719.                         (worm[i].statx * ENDXPIXEL) + FONTX + 4,
  2720.                         STARTYPIXEL + 1 + (4 * FONTY) + (worm[i].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
  2721.                     );
  2722.                     Image.ImageData = ImageData[POWER];
  2723.                     DrawImage
  2724.                     (   MainWindowPtr->RPort,
  2725.                         &Image,
  2726.                         (worm[i].statx * ENDXPIXEL) + FONTX + 4,
  2727.                         STARTYPIXEL + 1 + (5 * FONTY) + (worm[i].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
  2728.                     );
  2729.  
  2730.                     Image.ImageData = ImageData[ARMOUR];
  2731.                     DrawImage
  2732.                     (   MainWindowPtr->RPort,
  2733.                         &Image,
  2734.                         (worm[i].statx * ENDXPIXEL) + FONTX + 4,
  2735.                         STARTYPIXEL + 1 + (7 * FONTY) + (worm[i].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
  2736.                     );
  2737.                     Image.ImageData = ImageData[BIAS];
  2738.                     DrawImage
  2739.                     (   MainWindowPtr->RPort,
  2740.                         &Image,
  2741.                         (worm[i].statx * ENDXPIXEL) + FONTX + 4,
  2742.                         STARTYPIXEL + 1 + (8 * FONTY) + (worm[i].staty * (ENDYPIXEL - 1 - (11 * FONTY)))
  2743.                     );
  2744.         }   }   }
  2745.         else
  2746.         {   // assert(iso);
  2747.             if (worm[0].control != NONE || worm[2].control != NONE)
  2748.             {   Image.ImageData = ImageData[BONUS];
  2749.                 DrawImage(MainWindowPtr->RPort, &Image, 54, 12);
  2750.                 Image.ImageData = ImageData[LIFE];
  2751.                 DrawImage(MainWindowPtr->RPort, &Image, 54, 20);
  2752.                 Image.ImageData = ImageData[NITRO];
  2753.                 DrawImage(MainWindowPtr->RPort, &Image, 54, 28);
  2754.                 Image.ImageData = ImageData[AMMO];
  2755.                 DrawImage(MainWindowPtr->RPort, &Image, 54, 36);
  2756.                 Image.ImageData = ImageData[POWER];
  2757.                 DrawImage(MainWindowPtr->RPort, &Image, 54, 44);
  2758.                 Image.ImageData = ImageData[ARMOUR];
  2759.                 DrawImage(MainWindowPtr->RPort, &Image, 54, 52);
  2760.                 Image.ImageData = ImageData[BIAS];
  2761.                 DrawImage(MainWindowPtr->RPort, &Image, 54, 60);
  2762.             }
  2763.             if (worm[1].control != NONE || worm[3].control != NONE)
  2764.             {   Image.ImageData = ImageData[BONUS];
  2765.                 DrawImage(MainWindowPtr->RPort, &Image, 579, 190);
  2766.                 Image.ImageData = ImageData[LIFE];
  2767.                 DrawImage(MainWindowPtr->RPort, &Image, 579, 198);
  2768.                 Image.ImageData = ImageData[NITRO];
  2769.                 DrawImage(MainWindowPtr->RPort, &Image, 579, 206);
  2770.                 Image.ImageData = ImageData[AMMO];
  2771.                 DrawImage(MainWindowPtr->RPort, &Image, 579, 214);
  2772.                 Image.ImageData = ImageData[POWER];
  2773.                 DrawImage(MainWindowPtr->RPort, &Image, 579, 222);
  2774.                 Image.ImageData = ImageData[ARMOUR];
  2775.                 DrawImage(MainWindowPtr->RPort, &Image, 579, 230);
  2776.                 Image.ImageData = ImageData[BIAS];
  2777.                 DrawImage(MainWindowPtr->RPort, &Image, 579, 238);
  2778.         }   }
  2779.         for (i = 0; i <= 3; i++)
  2780.         {   icon(i, REMNANTS);
  2781.             icon(i, AFFIXER);
  2782.             icon(i, SIDESHOT);
  2783.             icon(i, PUSHER);
  2784.             icon(i, FREEDOM);
  2785.             icon(i, CUTTER);
  2786.             icon(i, ENCLOSER);
  2787. }   }   }
  2788.  
  2789. void datestamp(void)
  2790. {   ULONG            seconds, micros;
  2791.     struct ClockData Date;
  2792.     TEXT             temp[5];
  2793.  
  2794.     CurrentTime(&seconds, µs);
  2795.     Amiga2Date(seconds, &Date);
  2796.     stci_d(times, Date.hour);            /* hh */
  2797.     align(times, 2, ' ');
  2798.     times[2] = ':';                     /* hh: */
  2799.     times[3] = 0;
  2800.     stci_d(temp, Date.min);
  2801.     align(temp, 2, '0');
  2802.     temp[2] = 0;
  2803.     strcat(times, temp);                /* hh:mm */
  2804.  
  2805.     stci_d(date, Date.mday);            /* dd */
  2806.     align(date, 2, ' ');
  2807.     date[2] = '/';
  2808.     date[3] = 0;                        /* dd/ */
  2809.     stci_d(temp, Date.month);
  2810.     align(temp, 2, ' ');
  2811.     temp[2] = 0;
  2812.     strcat(date, temp);                    /* dd/mm */
  2813.     strcat(date, "/");                    /* dd/mm/ */
  2814.     stci_d(temp, Date.year);
  2815.     temp[0] = temp[2];
  2816.     temp[1] = temp[3];
  2817.     temp[2] = 0;
  2818.     strcat(date, temp);                    /* dd/mm/yy */
  2819. }
  2820.  
  2821. void turborender(void)
  2822. {   UBYTE random = rand() % 3;
  2823.     SBYTE x, y;
  2824.  
  2825.     if (a != PLAYGAME || (!level) || !(randomflag))
  2826.         sourcelevel = level;
  2827.  
  2828.     switch(random)
  2829.     {
  2830.     case 0:
  2831.         for (x = 0; x <= (FIELDX / 2) + 1; x++)
  2832.             for (y = 0; y <= FIELDY / 2; y++)
  2833.             {   draw(x, y, board[sourcelevel][x][y]);
  2834.                 draw(FIELDX - x, y, board[sourcelevel][FIELDX - x][y]);
  2835.                 draw(x, FIELDY - y, board[sourcelevel][x][FIELDY - y]);
  2836.                 draw(FIELDX - x, FIELDY - y, board[sourcelevel][FIELDX - x][FIELDY - y]);
  2837.             }
  2838.     break;
  2839.     case 1:
  2840.         for (y = 0; y <= FIELDY / 2; y++)
  2841.             for (x = 0; x <= (FIELDX / 2) + 1; x++)
  2842.             {   draw(x, y, board[sourcelevel][x][y]);
  2843.                 draw(FIELDX - x, y, board[sourcelevel][FIELDX - x][y]);
  2844.                 draw(x, FIELDY - y, board[sourcelevel][x][FIELDY - y]);
  2845.                 draw(FIELDX - x, FIELDY - y, board[sourcelevel][FIELDX - x][FIELDY - y]);
  2846.             }
  2847.     break;
  2848.     case 2:
  2849.         x = y = 0;
  2850.         do
  2851.         {   draw(x, y, board[sourcelevel][x][y]);
  2852.             x += APPEAR_CONSTANT;
  2853.             if (x >= FWIDTH)
  2854.             {   x -= FWIDTH;
  2855.                 y++;
  2856.                 if (y >= FHEIGHT)
  2857.                     y = 0;
  2858.         }   }
  2859.         while (x || y);
  2860.     break;
  2861.     default:
  2862.         assert(0);
  2863.     break;
  2864.     }
  2865.  
  2866.     if (a == FIELDEDIT)
  2867.     {   draw(startx[sourcelevel], starty[sourcelevel], START);
  2868.     } else
  2869.     {   for (y = 0; y <= FIELDY; y++)
  2870.             draw(ARROWX, y, BLACKENED);
  2871. }   }
  2872.  
  2873. MODULE void parsewb(void)
  2874. {   struct DiskObject* DiskObject;
  2875.     char**             ToolArray;
  2876.     char*              s;
  2877.  
  2878.     if ((*WBArg->wa_Name) && (DiskObject = GetDiskObject(WBArg->wa_Name)))
  2879.     {   ToolArray = (char **) DiskObject->do_ToolTypes;
  2880.  
  2881.         if (s = (char *) FindToolType(ToolArray, "NOPRELOAD"))
  2882.         {   fxable = musicable = DEFER;
  2883.         }
  2884.         if (s = (char *) FindToolType(ToolArray, "NOANIMS"))
  2885.         {   anims = FALSE;
  2886.         }
  2887.         if (s = (char *) FindToolType(ToolArray, "NOICONS"))
  2888.             icons = FALSE;
  2889.         if (s = (char *) FindToolType(ToolArray, "OVERHEAD"))
  2890.             iso = FALSE;
  2891.         if (s = (char *) FindToolType(ToolArray, "SHUFFLE"))
  2892.             randomflag = TRUE;
  2893.         if (s = (STRPTR) FindToolType(ToolArray, "QUIET"))
  2894.         {   quiet = TRUE;
  2895.         }
  2896.         if (s = (char *) FindToolType(ToolArray, "FILE"))
  2897.             strcpy(pathname, WBArg->wa_Name);
  2898.         if (s = (char *) FindToolType(ToolArray, "GREEN"))
  2899.         {   if (MatchToolValue(s, "KYBD"))
  2900.             {   worm[0].control = KEYBOARD;
  2901.             } elif (MatchToolValue(s, "JOY"))
  2902.             {   worm[0].control = JOYSTICK;
  2903.             } elif (MatchToolValue(s, "GAMEPAD"))
  2904.             {   worm[0].control = GAMEPAD;
  2905.             } elif (MatchToolValue(s, "AMIGA"))
  2906.             {   worm[0].control = AMIGA;
  2907.             } elif (MatchToolValue(s, "NONE"))
  2908.             {   worm[0].control = NONE;
  2909.         }   }
  2910.         if (s = (char *) FindToolType(ToolArray, "RED"))
  2911.         {   if (MatchToolValue(s, "KYBD"))
  2912.             {   worm[1].control = KEYBOARD;
  2913.             } elif (MatchToolValue(s, "JOY"))
  2914.             {   worm[1].control = JOYSTICK;
  2915.             } elif (MatchToolValue(s, "GAMEPAD"))
  2916.             {   worm[1].control = GAMEPAD;
  2917.             } elif (MatchToolValue(s, "AMIGA"))
  2918.             {   worm[1].control = AMIGA;
  2919.             } elif (MatchToolValue(s, "NONE"))
  2920.             {   worm[1].control = NONE;
  2921.         }   }
  2922.         if (s = (char *) FindToolType(ToolArray, "BLUE"))
  2923.         {   if (MatchToolValue(s, "JOY"))
  2924.             {   worm[2].control = JOYSTICK;
  2925.             } elif (MatchToolValue(s, "GAMEPAD"))
  2926.             {   worm[2].control = GAMEPAD;
  2927.             } elif (MatchToolValue(s, "AMIGA"))
  2928.             {   worm[2].control = AMIGA;
  2929.             } elif (MatchToolValue(s, "NONE"))
  2930.             {   worm[2].control = NONE;
  2931.         }   }
  2932.         if (s = (char *) FindToolType(ToolArray, "YELLOW"))
  2933.         {   if (MatchToolValue(s, "JOY"))
  2934.             {   worm[3].control = JOYSTICK;
  2935.             } elif (MatchToolValue(s, "GAMEPAD"))
  2936.             {   worm[3].control = GAMEPAD;
  2937.             } elif (MatchToolValue(s, "AMIGA"))
  2938.             {   worm[3].control = AMIGA;
  2939.             } elif (MatchToolValue(s, "NONE"))
  2940.             {   worm[3].control = NONE;
  2941.         }   }
  2942.         FreeDiskObject(DiskObject);
  2943. }   }
  2944.  
  2945. MODULE void pausetimer(void)
  2946. {    GetSysTime(CurrentValPtr);
  2947. }
  2948. MODULE void unpausetimer(void)
  2949. {    GetSysTime(PausedValPtr);
  2950.     SubTime(PausedValPtr, CurrentValPtr);
  2951.     AddTime(StartValPtr, PausedValPtr);
  2952. }
  2953.  
  2954. MODULE UBYTE ReadJoystick(UWORD joynum)
  2955. {   extern struct Custom far custom;
  2956.     UBYTE ret = 0;
  2957.     UWORD joy;
  2958.  
  2959.     if (joynum == 0)
  2960.         joy = custom.joy0dat;
  2961.     else joy = custom.joy1dat;
  2962.  
  2963.     ret += (joy >> 1 ^ joy) & 0x0100 ? JOYUP : 0;  
  2964.     ret += (joy >> 1 ^ joy) & 0x0001 ? JOYDOWN : 0;
  2965.     ret += joy & 0x0200 ? JOYLEFT : 0;
  2966.     ret += joy & 0x0002 ? JOYRIGHT : 0;
  2967.  
  2968.     if (joynum == 0)
  2969.     {   ret += !(CIAPtr->ciapra & 0x0040) ? JOYFIRE1 : 0; /* read firebuttons */
  2970.         ret += !(POTGOR & 0x0400) ? JOYFIRE2 : 0;         /* on joyport 0 */
  2971.     } else
  2972.     {   ret += !(CIAPtr->ciapra & 0x0080) ? JOYFIRE1 : 0; /* read firebuttons */
  2973.         ret += !(POTGOR & 0x0400) ? JOYFIRE2 : 0;         /* on joyport 1 */
  2974.     }
  2975.  
  2976.     return(ret);
  2977. }
  2978.  
  2979. AGLOBAL void ReadAdapterJoystick(UBYTE unit)
  2980. {   AUTO    UBYTE firebit, addroffset, snapshot;
  2981.     AUTO    SBYTE xx = 0, yy = 0;
  2982.     AUTO    ABOOL fire           = FALSE;
  2983.     PERSIST UBYTE oldsnapshot[2] = {0, 0};
  2984.     PERSIST ABOOL oldfire[2]     = {0, 0};
  2985.     PERSIST UBYTE *fireaddrPtr,
  2986.                   *moveaddrPtr;
  2987.  
  2988.     fireaddrPtr = 0xBFD000;
  2989.     moveaddrPtr = 0xBFE101;
  2990.  
  2991. /*  Those pointers could easily be auto or constant or whatever; they
  2992.     never change.
  2993.  
  2994.     bit 7 of $bfe101 is right of joystick '4'
  2995.     bit 6 of $bfe101 is left  of joystick '4'
  2996.     bit 5 of $bfe101 is down  of joystick '4'
  2997.     bit 4 of $bfe101 is up    of joystick '4'
  2998.     bit 3 of $bfe101 is right of joystick '3'
  2999.     bit 2 of $bfe101 is left  of joystick '3'
  3000.     bit 1 of $bfe101 is down  of joystick '3'
  3001.     bit 0 of $bfe101 is up    of joystick '3'
  3002.  
  3003.     bit 2 of $bfd000 is fire  of joystick '3'
  3004.     bit 0 of $bfd000 is fire  of joystick '4'
  3005.  
  3006.     unit: number of the unit (2 = '3', 3 = '4') */
  3007.  
  3008.     if (unit == 2)
  3009.     {   firebit = 2;
  3010.         addroffset = 0;
  3011.     } else
  3012.     {   // assert(unit == 3);
  3013.         firebit = 0;
  3014.         addroffset = 4;
  3015.     }
  3016.  
  3017.     if (worm[unit].control == JOYSTICK && worm[unit].lives)
  3018.     {   if ((*fireaddrPtr) & (1 << firebit))
  3019.             fire = TRUE;
  3020.  
  3021.         snapshot = *moveaddrPtr; // we cache this address in the snapshot
  3022.                                  // variable, so that joystick reading is an
  3023.                                  // atomic operation
  3024.         if (snapshot & (1 << (addroffset + 3)))
  3025.             xx = 1;
  3026.         elif (snapshot & (1 << (addroffset + 2)))
  3027.             xx = -1;
  3028.         if (snapshot & (1 << (addroffset + 1)))
  3029.             yy = 1;
  3030.         elif (snapshot & (1 <<  addroffset     ))
  3031.             yy = -1;
  3032.  
  3033.         if (oldsnapshot[unit - 2] != snapshot || oldfire[unit - 2] != fire)
  3034.         {   if (fire)
  3035.                 wormqueue(unit, 0, 0);
  3036.             else
  3037.                 wormqueue(unit, xx, yy);
  3038.         }
  3039.         oldsnapshot[unit - 2] = snapshot;
  3040.         oldfire[unit - 2]     = fire;
  3041. }   }
  3042.  
  3043. MODULE ABOOL firebutton(void)
  3044. {   UBYTE PortState = ReadJoystick(1);
  3045.  
  3046.     if (PortState & JOYFIRE1) // don't check JOYFIRE2
  3047.     {   return(TRUE);
  3048.     } else return(FALSE);
  3049. }
  3050.  
  3051. AGLOBAL void ReadStandardJoystick(ULONG port)
  3052. {   AUTO    UBYTE PortState[2];
  3053.     AUTO    ABOOL fire            = FALSE;
  3054.     AUTO    SBYTE xx              = 0,
  3055.                   yy              = 0,
  3056.                   player;
  3057.     PERSIST UBYTE OldPortState[2] = {0, 0};
  3058.  
  3059.     if (port == 1)
  3060.     {   player = 2; // blue worm
  3061.     } else
  3062.     {   // assert(port == 0);
  3063.         player = 3; // yellow worm
  3064.     }
  3065.     if (worm[player].control == JOYSTICK && worm[player].lives)
  3066.     {   PortState[port] = ReadJoystick(port);
  3067.         if (PortState[port] != OldPortState[port])
  3068.         {   if (PortState[port] & JOYUP)
  3069.                 yy = -1;
  3070.             elif (PortState[port] & JOYDOWN)
  3071.                 yy = 1;
  3072.             if (PortState[port] & JOYLEFT)
  3073.                 xx = -1;
  3074.             elif (PortState[port] & JOYRIGHT)
  3075.                 xx = 1;
  3076.             if (PortState[port] & JOYFIRE1)
  3077.             {   fire = TRUE;
  3078.             }
  3079.  
  3080.             // this doesn't work perfectly if the player does have nitro...
  3081.             if (PortState[port] != 0 && (worm[player].nitro || xx == 0 || yy == 0))
  3082.             // if joystick is off-centre or firebutton is down
  3083.             {   if (fire)                      // if firebutton is down
  3084.                 {   wormqueue(player, 0, 0);   // then shoot/jump
  3085.                 } else                         // if firebutton is up
  3086.                 {   wormqueue(player, xx, yy); // then move
  3087.         }   }   }
  3088.         OldPortState[port] = PortState[port];
  3089. }   }
  3090.  
  3091. void flash(ULONG where)
  3092. {   TEXT saystring[SAYLIMIT + 1];
  3093.  
  3094.     stci_d(saystring, where);
  3095.     say(saystring, PURPLE);
  3096.     while(1);
  3097. }
  3098.  
  3099. AGLOBAL void ReadGamepads(void)
  3100. {   AUTO    ULONG PortState;
  3101.     AUTO    ABOOL fire = FALSE;
  3102.     AUTO    SBYTE i, xx = 0, yy = 0;
  3103.     PERSIST ULONG OldPortState = 0;
  3104.  
  3105.     for (i = 0; i <= 3; i++)
  3106.     {   if (worm[i].control == GAMEPAD && worm[i].lives)
  3107.         {   PortState = ReadJoyPort(worm[i].port);
  3108.             if (PortState != OldPortState)
  3109.             {   if (PortState & JP_BUTTON_MASK)
  3110.                 {   fire = TRUE;
  3111.                 }
  3112.                 if (PortState & JPF_JOY_UP)
  3113.                 {   yy = -1;
  3114.                 } elif (PortState & JPF_JOY_DOWN)
  3115.                 {   yy = 1;
  3116.                 }
  3117.                 if (PortState & JPF_JOY_LEFT)
  3118.                 {   xx = -1;
  3119.                 } elif (PortState & JPF_JOY_RIGHT)
  3120.                 {   xx = 1;
  3121.                 }
  3122.                 if (fire)                 // if firebutton is down
  3123.                 {   wormqueue(i, 0, 0);   // then shoot/jump
  3124.                 } elif (xx || yy)
  3125.                 {   wormqueue(i, xx, yy);
  3126.             }   }
  3127.             OldPortState = PortState;
  3128. }   }   }
  3129.  
  3130.